diff options
Diffstat (limited to 'cpukit/libmisc')
211 files changed, 37431 insertions, 0 deletions
diff --git a/cpukit/libmisc/.cvsignore b/cpukit/libmisc/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/cpukit/libmisc/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/cpukit/libmisc/Makefile.am b/cpukit/libmisc/Makefile.am new file mode 100644 index 0000000000..f288f14f2b --- /dev/null +++ b/cpukit/libmisc/Makefile.am @@ -0,0 +1,161 @@ +## +## $Id$ +## + +include $(top_srcdir)/automake/compile.am + +EXTRA_DIST = README + +noinst_LIBRARIES = + +## bspcmdline +noinst_LIBRARIES += libbspcmdline.a +libbspcmdline_a_SOURCES = bspcmdline/bspcmdline_get.c \ + bspcmdline/bspcmdline_getparam.c bspcmdline/bspcmdline_getparamraw.c \ + bspcmdline/bspcmdline_getparamrhs.c + +## capture +EXTRA_DIST += capture/README + +noinst_LIBRARIES += libcapture.a +libcapture_a_SOURCES = capture/capture.c capture/capture-cli.c \ + capture/capture.h capture/capture-cli.h + +## cpuuse +EXTRA_DIST += cpuuse/README + +noinst_LIBRARIES += libcpuuse.a +libcpuuse_a_SOURCES = cpuuse/cpuusagereport.c cpuuse/cpuusagereset.c \ + cpuuse/cpuuse.h cpuuse/cpuusagedata.c + +## devnull +noinst_LIBRARIES += libdevnull.a +libdevnull_a_SOURCES = devnull/devnull.c devnull/devnull.h + +## dummy +EXTRA_DIST += dummy/README + +noinst_LIBRARIES += libdummy.a +libdummy_a_SOURCES = dummy/dummy.c dummy/dummy-networking.c + +## dumpbuf +noinst_LIBRARIES += libdumpbuf.a +libdumpbuf_a_SOURCES = dumpbuf/dumpbuf.c dumpbuf/dumpbuf.h + +## monitor +noinst_LIBRARIES += libmonitor.a +libmonitor_a_SOURCES = monitor/mon-command.c monitor/mon-symbols.c \ + monitor/mon-prmisc.c monitor/mon-monitor.c monitor/mon-object.c \ + monitor/mon-server.c monitor/mon-task.c monitor/mon-queue.c \ + monitor/mon-driver.c monitor/mon-itask.c monitor/mon-extension.c \ + monitor/mon-manager.c monitor/mon-config.c monitor/mon-part.c \ + monitor/mon-region.c monitor/mon-sema.c monitor/mon-editor.c \ + monitor/symbols.h monitor/monitor.h +if LIBNETWORKING +libmonitor_a_SOURCES += monitor/mon-network.c +endif +if HAS_MP +libmonitor_a_SOURCES += monitor/mon-mpci.c +endif + +EXTRA_DIST += monitor/README + +## fb +noinst_LIBRARIES += libmw-fb.a +libmw_fb_a_SOURCES = fb/mw_print.c fb/mw_uid.c fb/fb.h fb/mw_uid.h + +## mouse +noinst_LIBRARIES += libmouse.a +libmouse_a_SOURCES = mouse/mouse_parser.c mouse/serial_mouse.c +EXTRA_DIST += mouse/README + +## shell +if LIBSHELL +noinst_LIBRARIES += libshell.a +libshell_a_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/shell +libshell_a_SOURCES = shell/cat_file.c shell/cmds.c shell/internal.h \ + shell/main_alias.c shell/main_cat.c shell/main_cd.c shell/cmp-ls.c \ + shell/main_chdir.c shell/main_chmod.c shell/main_chroot.c \ + shell/main_cp.c shell/main_cpuuse.c shell/main_date.c shell/main_dir.c \ + shell/main_echo.c shell/main_exit.c shell/main_halt.c shell/main_help.c \ + shell/main_id.c shell/main_logoff.c shell/main_ln.c shell/main_ls.c \ + shell/main_mallocinfo.c shell/main_mdump.c shell/main_medit.c \ + shell/main_mfill.c shell/main_mkdir.c shell/main_mount.c \ + shell/main_mmove.c shell/main_msdosfmt.c \ + shell/main_mv.c shell/main_mwdump.c shell/main_perioduse.c \ + shell/main_pwd.c shell/main_rm.c shell/main_rmdir.c shell/main_sleep.c \ + shell/main_stackuse.c shell/main_tty.c shell/main_umask.c \ + shell/main_unmount.c shell/main_blksync.c shell/main_whoami.c \ + shell/shell.c shell/shell_cmdset.c shell/shell_getchar.c \ + shell/shell_getprompt.c shell/shellconfig.c \ + shell/shellconfig.h shell/shell.h shell/shell_makeargs.c \ + shell/filemode.c shell/pwcache.c shell/print-ls.c \ + shell/write_file.c shell/utils-cp.c shell/utils-ls.c \ + shell/err.c shell/errx.c shell/verr.c shell/vis.c \ + shell/verrx.c shell/vwarn.c shell/vwarnx.c shell/warn.c shell/warnx.c \ + shell/fts.c shell/print_heapinfo.c shell/main_wkspaceinfo.c \ + shell/shell_script.c shell/login_prompt.c shell/login_check.c \ + shell/fdisk.c shell/main_rtc.c \ + shell/dd-args.c shell/main_dd.c shell/dd-conv.c shell/dd-conv_tab.c \ + shell/dd-misc.c shell/dd-position.c \ + shell/main_hexdump.c shell/hexdump-conv.c shell/hexdump-display.c \ + shell/hexdump-odsyntax.c shell/hexdump-parse.c shell/hexsyntax.c \ + shell/main_time.c shell/main_mknod.c \ + shell/main_setenv.c shell/main_getenv.c shell/main_unsetenv.c \ + shell/main_mkrfs.c shell/main_debugrfs.c + +if LIBNETWORKING +libshell_a_SOURCES += \ + shell/main_ifconfig.c shell/main_route.c shell/main_netstats.c +endif +endif + +EXTRA_DIST += shell/README + +## serdbg + +if LIBSERDBG +noinst_LIBRARIES += libserdbg.a +libserdbg_a_SOURCES = serdbg/serdbg.c serdbg/serdbgio.c \ + serdbg/termios_printk.c serdbg/serdbgcnf.h serdbg/serdbg.h \ + serdbg/termios_printk_cnf.h serdbg/termios_printk.h +endif + +EXTRA_DIST += serdbg/README + +## stackchk +noinst_LIBRARIES += libstackchk.a +libstackchk_a_SOURCES = stackchk/check.c stackchk/internal.h \ + stackchk/stackchk.h + +EXTRA_DIST += stackchk/README + +## libuntar +noinst_LIBRARIES += libuntar.a +libuntar_a_SOURCES = untar/untar.c untar/untar.h + +EXTRA_DIST += untar/README + +## stringto +noinst_LIBRARIES += libstringto.a +libstringto_a_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/stringto +libstringto_a_SOURCES = stringto/stringtodouble.c stringto/stringtofloat.c \ + stringto/stringtolongdouble.c \ + stringto/stringtoint.c stringto/stringtolong.c stringto/stringtolonglong.c \ + stringto/stringtopointer.c stringto/stringtounsignedint.c \ + stringto/stringtounsignedchar.c stringto/stringtounsignedlong.c \ + stringto/stringtounsignedlonglong.c + +## fsmount +noinst_LIBRARIES += libfsmount.a +libfsmount_a_SOURCES = fsmount/fsmount.c fsmount/fsmount.h + +## uuid +noinst_LIBRARIES += libuuid.a +libuuid_a_SOURCES = uuid/clear.c uuid/compare.c uuid/copy.c uuid/gen_uuid.c \ + uuid/isnull.c uuid/pack.c uuid/parse.c uuid/unpack.c uuid/unparse.c \ + uuid/uuid_time.c uuid/uuidd.h uuid/uuidP.h + +## --- +include $(srcdir)/preinstall.am +include $(top_srcdir)/automake/local.am diff --git a/cpukit/libmisc/README b/cpukit/libmisc/README new file mode 100644 index 0000000000..bbde4feeae --- /dev/null +++ b/cpukit/libmisc/README @@ -0,0 +1,23 @@ +# +# $Id$ +# + +This directory contains for the "miscellaneous" library. Currently +this library contains a number of useful support libraries: + + + Task Stack Overflow Checker + + Workspace Consistency Checker + + Task Execution Time Monitor + + Period Statistics Monitor + + Debug Monitor + +The following ideas have been mentioned for items which could go +in this library, but this list is not all inclusive: + + + there are no outstanding suggestions. + +The intent of this library is to provide a home for useful utility routines +which are dependent upon RTEMS. + +--joel +16 Sept 97 diff --git a/cpukit/libmisc/bspcmdline/bspcmdline.h b/cpukit/libmisc/bspcmdline/bspcmdline.h new file mode 100644 index 0000000000..6d5f716aeb --- /dev/null +++ b/cpukit/libmisc/bspcmdline/bspcmdline.h @@ -0,0 +1,128 @@ +/** + * @file rtems/bspcmdline.h + * + * This include file contains all prototypes and specifications + * related to the BSP Command Line String and associated helper + * routines. The helpers are useful for locating command line + * type arguments (e.g. --mode) and their associated right + * hand side (e.g. FAST in --mode=FAST). + */ + +/* + * COPYRIGHT (c) 1989-2009. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef __BSP_COMMAND_LINE_h +#define __BSP_COMMAND_LINE_h + +/** + * @defgroup BSPCommandLine BSP Command Line Helpers + * + * The BSP Command Line Handler provides a set of routines which assist + * in examining and decoding the Command Line String passed to the BSP + * at boot time. + */ +/**@{*/ + +#include <stddef.h> /* for size_t */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @brief Obtain Pointer to BSP Boot Command String + * + * This method returns a pointer to the BSP Boot Command String. It + * is as likely to be NULL as point to a string as most BSPs do not + * have a start environment that provides a boot string. + * + * @return This method returns the pointer to the BSP Boot Command String. + */ +const char *rtems_bsp_cmdline_get(void); + +/** + * @brief Obtain COPY of the Entire Matching Argument + * + * This method searches for the argument @a name in the BSP Boot Command + * String and returns a copy of the entire string associated with it in + * @a value up to a string of @a length. This will include the argument + * and any right hand side portion of the string. For example, one might + * be returned --mode=FAST if + * searching for --mode. + * + * @param[in] name is the arugment to search for + * @param[in] value points to where the contents will + * be placed if located. + * @param[in] length is the maximum length to copy + * + * @return This method returns NULL if not found and + * @a value if found. + */ +const char *rtems_bsp_cmdline_get_param( + const char *name, + char *value, + size_t length +); + + +/** + * @brief Obtain COPY of the Right Hand Side of the Matching Argument + * + * This method searches for the argument @a name in + * the BSP Boot Command String and returns the right hand side + * associated with it in @a value up to a maximum string @a length. + * This will NOT include the argument but only any right hand side + * portion of the string. * For example, one might be returned FAST if + * searching for --mode. + * + * @param[in] name is the arugment to search for + * @param[in] value points to where the contents will + * be placed if located. + * @param[in] length is the maximum length to copy + * + * @return This method returns NULL if not found and + * @a value if found. + */ +const char *rtems_bsp_cmdline_get_param_rhs( + const char *name, + char *value, + size_t length +); + +/** + * @brief Obtain Pointer to the Entire Matching Argument + * + * This method searches for the argument @a name in + * the BSP Boot Command String and returns a pointer to the + * entire string associated with it. This will include the + * argument and any right hand side portion of the string. + * For example, one might be returned --mode=FAST if + * searching for --mode. + * + * @param[in] name is the arugment to search for + * + * @return This method returns NULL if not found and a pointer + * into the BSP Boot Command String if found. + * + * @note The pointer will be to the original BSP Command + * Line string. Exercise caution when using this. + */ +const char *rtems_bsp_cmdline_get_param_raw( + const char *name +); + +#ifdef __cplusplus +} +#endif + +/**@}*/ +#endif diff --git a/cpukit/libmisc/bspcmdline/bspcmdline_get.c b/cpukit/libmisc/bspcmdline/bspcmdline_get.c new file mode 100644 index 0000000000..7e06aa47bd --- /dev/null +++ b/cpukit/libmisc/bspcmdline/bspcmdline_get.c @@ -0,0 +1,23 @@ +/* + * COPYRIGHT (c) 1989-2009. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/bspcmdline.h> + +extern const char *bsp_boot_cmdline; + +const char *rtems_bsp_cmdline_get(void) +{ + return bsp_boot_cmdline; +} diff --git a/cpukit/libmisc/bspcmdline/bspcmdline_getparam.c b/cpukit/libmisc/bspcmdline/bspcmdline_getparam.c new file mode 100644 index 0000000000..288128bf8d --- /dev/null +++ b/cpukit/libmisc/bspcmdline/bspcmdline_getparam.c @@ -0,0 +1,67 @@ +/* + * COPYRIGHT (c) 1989-2009. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/bspcmdline.h> + +static void copy_string( + const char *start, + char *value, + size_t length +) +{ + int i; + int quotes; + const char *p = start; + + quotes=0; + for (i=0 ; *p && i<length-1; ) { + if ( *p == '\"' ) { + quotes++; + } else if ( ((quotes % 2) == 0) && *p == ' ' ) + break; + value[i++] = *p++; + value[i] = '\0'; + } + +} + +const char *rtems_bsp_cmdline_get_param( + const char *name, + char *value, + size_t length +) +{ + const char *p; + + if ( !name ) + return NULL; + + if ( !value ) + return NULL; + + if ( !length ) + return NULL; + + value[0] = '\0'; + + p = rtems_bsp_cmdline_get_param_raw( name ); + + if ( !p ) + return NULL; + + copy_string( p, value, length ); + + return value; +} diff --git a/cpukit/libmisc/bspcmdline/bspcmdline_getparamraw.c b/cpukit/libmisc/bspcmdline/bspcmdline_getparamraw.c new file mode 100644 index 0000000000..18bda9143f --- /dev/null +++ b/cpukit/libmisc/bspcmdline/bspcmdline_getparamraw.c @@ -0,0 +1,37 @@ +/* + * COPYRIGHT (c) 1989-2009. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include <rtems/bspcmdline.h> + +extern const char *bsp_boot_cmdline; + +const char *rtems_bsp_cmdline_get_param_raw( + const char *name +) +{ + const char *p; + + if ( !name ) + return NULL; + + if ( !bsp_boot_cmdline ) + return NULL; + + p = strstr(bsp_boot_cmdline, name); + /* printf( "raw: %p (%s)\n", p, p ); */ + return p; +} diff --git a/cpukit/libmisc/bspcmdline/bspcmdline_getparamrhs.c b/cpukit/libmisc/bspcmdline/bspcmdline_getparamrhs.c new file mode 100644 index 0000000000..38269dc44a --- /dev/null +++ b/cpukit/libmisc/bspcmdline/bspcmdline_getparamrhs.c @@ -0,0 +1,48 @@ +/* + * COPYRIGHT (c) 1989-2009. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include <rtems/bspcmdline.h> + +const char *rtems_bsp_cmdline_get_param_rhs( + const char *name, + char *value, + size_t length +) +{ + const char *p; + const char *rhs; + char *d; + + p = rtems_bsp_cmdline_get_param( name, value, length ); + if ( !p ) + return NULL; + + rhs = &p[strlen(name)]; + if ( *rhs != '=' ) + return NULL; + + rhs++; + if ( *rhs == '\"' ) + rhs++; + for ( d=value ; *rhs ; ) + *d++ = *rhs++; + if ( *(d-1) == '\"' ) + d--; + *d = '\0'; + + return value; +} diff --git a/cpukit/libmisc/capture/README b/cpukit/libmisc/capture/README new file mode 100644 index 0000000000..d12b5570d6 --- /dev/null +++ b/cpukit/libmisc/capture/README @@ -0,0 +1,255 @@ +# +# $Id$ +# + + RTEMS Performance Monitoring and Measurement Framework + + Copyright 2002-2007 Chris Johns (chrisj@rtems.org) + 23 April 2002 + +This directory contains the source code for the performance monitoring and +measurement framework. It is more commonly know as the capture engine. + +The capture engine is in an early phase of development. Please review the Status +section of this document for the current status. + +Performance. + +The capture engine is designed to not effect the system it is +monitoring. Resources such as memory are used as well as a small performance +hit in task creation, deletion and context switch. The overhead is small and +will not be noticed unless the system is operating close to the performance +limit of the target. + +Structure. + +The capture engine is implemented in a couple of layers. This lowest layer is +the capture engine. Its interface is in the file 'capture.h'. Typically this +interface is directly used unless you are implementing a target interface. The +user interface is via a target interface. + +Command Line Interface (CLI). + +This is a target interface that provides a number of user commands via the +RTEMS monitor. To use you need to provide the following in your +application initialisation: + + #include <rtems/monitor.h> + #include <rtems/capture-cli.h> + + rtems_monitor_init (0); + rtems_capture_cli_init (0); + +Check the file capture-cli.h for documentation of the interface. The parameter +is a pointer to your board support package's time stamp handler. The time stamp +handler is yet to be tested so it is recommended this is left as 0, unless you +wish to test this part of the engine. + +The commands are: + + copen - Open the capture engine. + cclose - Close the capture engine. + cenable - Enable the capture engine. + cdisable - Disable the capture engine. + ctlist - List the tasks known to the capture engine. + ctload - Display the current load (sort of top). + cwlist - List the watch and trigger controls. + cwadd - Add a watch. + cwdel - Delete a watch. + cwctl - Enable or disable a watch. + cwglob - Enable or disable the global watch. + cwceil - Set the watch ceiling. + cwfloor - Set the watch floor. + ctrace - Dump the trace records. + ctrig - Define a trigger. + +Open + + usage: copen [-i] size + +Open the capture engine. The size parameter is the size of the capture engine +trace buffer. A single record hold a single event, for example a task create or +a context in or out. The option '-i' will enable the capture engine after it is +opened. + +Close + + usage: cclose + +Close the capture engine and release all resources held by the capture engine. + +Enable + + usage: cenable + +Enable the capture engine if it has been opened. + +Disable + + usage: cdisable + +Disable the capture engine. The enable and disable commands provide a means of +removing the overhead of the capture engine from the context switch. This may +be needed when testing if it is felt the capture engines overhead is effecting +the system. + +Task List + + usage: ctlist + +List the tasks the capture engine knows about. This may contain tasks that have +been deleted. + +Task Load + + usage: ctload + +List the tasks in the order of load in a similar way top does on Unix. The +command sends ANSI terminal codes. You press enter to stop the update. The +update period is fixed at 5 seconds. The output looks like: + + Press ENTER to exit. + + PID NAME RPRI CPRI STATE %CPU %STK FLGS EXEC TIME +04010001 IDLE 255 255 READY 96.012% 0% a-----g 1 +08010009 CPlt 1 1 READY 3.815% 15% a------ 0 +08010003 ntwk 20 20 Wevnt 0.072% 0% at----g 0 +08010004 CSr0 20 20 Wevnt 0.041% 0% at----g 0 +08010001 main 250 250 DELAY 0.041% 0% a-----g 0 +08010008 test 100 100 Wevnt 0.000% 20% at-T-+g 0 +08010007 test 100 100 Wevnt 0.000% 0% at-T-+g 0 +08010005 CSt0 20 20 Wevnt 0.000% 0% at----g 0 +08010006 RMON 1 1 Wsem 0.000% 0% a------ 0 + +There are 7 flags and from left to right are: + +1) 'a' the task is active, and 'd' the task has been deleted. +2) 't' the task has been traced. +3) 'F' the task has a from (TO_ANY) trigger. +4) 'T' the task has a to (FROM_ANY) trigger. +5) 'E' the task has an edge (FROM_TO) trigger. +6) '+' the task as a watch control attached, 'w' a watch is enabled. +7) 'g' the task is part of a global trigger. + +The %STK is the percentage of stack used by a task. Currently only tasks +created while the capture engine is enabled can be monitored. + +The RPRI is the real priority. This is the priority set for the task. The +current priority is the executing priority that may reflect a level set as a +result of priority inversion. + +Watch List + + usage: cwlist + +This command lists the watch and trigger controls the capture engine has. A +control is a structure used by the capture engine to determine if a task is +watched or triggers capturing. + +Watch Add + + usage: cwadd [task name] [id] + +Add a watch for a task. You can provide a name or id or both. A name will cause +all tasks with that name to have the watch added. An id results in a watch +being for a specific task. + +Using a name is useful when the task is not yet created. + +Watch Delete + + usage: cwdel [task name] [id] + +Delete a watch that has been added. + +Watch Control + + usage: cwctl [task name] [id] on/off + +Enable or disable a watch. The name and id parameters are the same as the watch +add command. + +Global Watch + + usage: cwglob on/off + +Enable or disable the global watch. A global watch is an easy way to enable +watches for all tasks with real priorities between the watch ceiling and floor +priorities. + +Watch Priority Ceiling + + usage: cwceil priority + +Set the watch priority ceiling. All tasks with a priority less than the ceiling +priority are not watched. This allow you to ignore high priority system and +driver tasks. + +Watch Priority Floor + + usage: cwfloor priority + +Set the watch priority floor. All tasks with a priority greater than the floor +priority level are not watched. This allows you to remove tasks such as IDLE +from being monitored. + +Trace + + usage: ctrace [-c] [-r records] + +Dump the trace record. The option '-c' will output the records in comma +separated variables (CSV). The '-r' option controls the number of records +dumped. This can help stop the command looping for-ever. + +Trigger + + usage: ctrig type [from name] [from id] [to name] [to id] + +Set a trigger. The types of triggers are : + + from : trigger on a context switch from a task + to : trigger on a context switch to a task + edge : trigger on a context switch from a task to a task + +The from and to trigger types requires a task name or task id or both be +provided. The edge requires a from name and/or id and a to name and/or id be +provided. + +Flush + + usage: cflush [-n] + +Flush the trace record. The option '-n' stops the capture engine be +primed. This means an exising trigger state will not be cleared and tracing +will continue. + +Status. + +The following is a list of outstanding issues or bugs. + +1) The capture engine does not scan the existing list of tasks in the kernel + when initialised. This means tasks that exist but are not active are not + seen. Not sure how to implement this one. + +2) The blocking read of trace records has not been completely implemented or + tested. This will wait until I complete the csv support for the cli for a + serial UI or the tcp server is implemented. + +3) Task control block clean up is not implemented. The control block should be + dumped to the trace buffer. This requires extended record formats. This can + be implemented using an event flag to indicate an extended record follows + the trace record. This would allow a task delete record to be directly + followed by the task information. + +4) Complete csv (comma separated variable) support for the CLI. + +5) Implement a tcp server interface. + +6) Complete the capture engine API documentation. + +7) Test the user supplied time stamp handler. + +8) Task name support is only for the rtems_name type. This means the only the + classic API tasks are currently supported. Partial support for the different + task names is provided how-ever this is not clean and does not support the + variable length task name such as found in the POSIX tasks. diff --git a/cpukit/libmisc/capture/capture-cli.c b/cpukit/libmisc/capture/capture-cli.c new file mode 100644 index 0000000000..98f76764f3 --- /dev/null +++ b/cpukit/libmisc/capture/capture-cli.c @@ -0,0 +1,1622 @@ +/* + ------------------------------------------------------------------------ + $Id$ + ------------------------------------------------------------------------ + + Copyright Objective Design Systems Pty Ltd, 2002 + All rights reserved Objective Design Systems Pty Ltd, 2002 + Chris Johns (ccj@acm.org) + + COPYRIGHT (c) 1989-1998. + On-Line Applications Research Corporation (OAR). + + The license and distribution terms for this file may be + found in the file LICENSE in this distribution. + + This software with is provided ``as is'' and with NO WARRANTY. + + ------------------------------------------------------------------------ + + RTEMS Performance Monitoring and Measurement Framework. + + This is the Target Interface Command Line Interface. You need + start the RTEMS monitor. + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + +#include <rtems.h> +#include <rtems/capture-cli.h> +#include <rtems/monitor.h> + +#define RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS (20) + +/* + * The user capture timestamper. + */ +static rtems_capture_timestamp capture_timestamp; + +/* + * Common variable to sync the load monitor task. + */ +static volatile int cli_load_thread_active; + +/* + * rtems_capture_cli_open + * + * DESCRIPTION: + * + * This function opens the capture engine. We need the size of the + * capture buffer. + * + */ + +static const char* open_usage = "usage: copen [-i] size\n"; + +static void +rtems_capture_cli_open (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + uint32_t size = 0; + bool enable = false; + rtems_status_code sc; + int arg; + + if (argc <= 1) + { + fprintf (stdout, open_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + if (argv[arg][1] == 'i') + enable = true; + else + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + size = strtoul (argv[arg], 0, 0); + + if (size < 100) + { + fprintf (stdout, "error: size must be greater than or equal to 100\n"); + return; + } + } + } + + sc = rtems_capture_open (size, capture_timestamp); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: open failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "capture engine opened.\n"); + + if (!enable) + return; + + sc = rtems_capture_control (enable); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: open enable failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "capture engine enabled.\n"); +} + +/* + * rtems_capture_cli_close + * + * DESCRIPTION: + * + * This function closes the capture engine. + * + */ + +static void +rtems_capture_cli_close (int argc __attribute__((unused)), + char** argv __attribute__((unused)), + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + + sc = rtems_capture_close (); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: close failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "capture engine closed.\n"); +} + +/* + * rtems_capture_cli_enable + * + * DESCRIPTION: + * + * This function enables the capture engine. + * + */ + +static void +rtems_capture_cli_enable (int argc __attribute__((unused)), + char** argv __attribute__((unused)), + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + + sc = rtems_capture_control (1); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: enable failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "capture engine enabled.\n"); +} + +/* + * rtems_capture_cli_disable + * + * DESCRIPTION: + * + * This function disables the capture engine. + * + */ + +static void +rtems_capture_cli_disable (int argc __attribute__((unused)), + char** argv __attribute__((unused)), + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + + sc = rtems_capture_control (0); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: disable failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "capture engine disabled.\n"); +} + +/* + * rtems_capture_cli_task_list + * + * DESCRIPTION: + * + * This function lists the tasks the capture engine knows about. + * + */ + +static void +rtems_capture_cli_task_list (int argc __attribute__((unused)), + char** argv __attribute__((unused)), + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_task_priority ceiling = rtems_capture_watch_get_ceiling (); + rtems_task_priority floor = rtems_capture_watch_get_floor (); + rtems_capture_task_t* task = rtems_capture_get_task_list (); + uint32_t ticks; + uint32_t tick_offset; + unsigned long long total_time; + int count = rtems_capture_task_count (); + + if (capture_timestamp) + capture_timestamp (&ticks, &tick_offset); + else + { + ticks = _Watchdog_Ticks_since_boot; + tick_offset = 0; + } + + fprintf (stdout, "total %i\n", count); + + while (task) + { + rtems_task_priority priority; + int32_t stack_used; + int32_t time_used; + + stack_used = rtems_capture_task_stack_usage (task); + if (stack_used) + stack_used = (stack_used * 100) / stack_used; + + if (stack_used > 100) + stack_used = 100; + + total_time = (ticks * rtems_capture_task_time (task)) + tick_offset; + + time_used = (rtems_capture_task_time (task) * 100) / total_time; + + if (time_used > 100) + time_used = 100; + + priority = rtems_capture_task_real_priority (task); + + fprintf (stdout, " "); + rtems_monitor_dump_id (rtems_capture_task_id (task)); + fprintf (stdout, " "); + rtems_monitor_dump_name (rtems_capture_task_name (task)); + fprintf (stdout, " "); + rtems_monitor_dump_priority (rtems_capture_task_start_priority (task)); + fprintf (stdout, " "); + rtems_monitor_dump_priority (rtems_capture_task_real_priority (task)); + fprintf (stdout, " "); + rtems_monitor_dump_priority (rtems_capture_task_curr_priority (task)); + fprintf (stdout, " "); + rtems_monitor_dump_state (rtems_capture_task_state (task)); + fprintf (stdout, " %c%c", + rtems_capture_task_valid (task) ? 'a' : 'd', + rtems_capture_task_flags (task) & RTEMS_CAPTURE_TRACED ? 't' : '-'); + + if ((floor > ceiling) && (ceiling > priority)) + fprintf (stdout, "--"); + else + { + uint32_t flags = rtems_capture_task_control_flags (task); + fprintf (stdout, "%c%c", + rtems_capture_task_control (task) ? + (flags & RTEMS_CAPTURE_WATCH ? 'w' : '+') : '-', + rtems_capture_watch_global_on () ? 'g' : '-'); + } + fprintf (stdout, " %3" PRId32 "%% %3" PRId32 "%% (%" PRIu32 ")\n", + stack_used, time_used, rtems_capture_task_ticks (task)); + + task = rtems_capture_next_task (task); + } +} + +/* + * rtems_capture_cli_task_load_thread + * + * DESCRIPTION: + * + * This function displays the load of the tasks on an ANSI terminal. + * + */ + +static void +rtems_capture_cli_task_load_thread (rtems_task_argument arg __attribute__((unused))) +{ + rtems_task_priority ceiling = rtems_capture_watch_get_ceiling (); + rtems_task_priority floor = rtems_capture_watch_get_floor (); + int last_count = 0; + + for (;;) + { + rtems_capture_task_t* tasks[RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS + 1]; + unsigned long long load[RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS + 1]; + rtems_capture_task_t* task; + unsigned long long total_time; + int count = 0; + int i; + int j; + + cli_load_thread_active = 1; + + /* + * Iterate over the tasks and sort the highest load tasks + * into our local arrays. We only handle a limited number of + * tasks. + */ + + memset (tasks, 0, sizeof (tasks)); + memset (load, 0, sizeof (load)); + + task = rtems_capture_get_task_list (); + + total_time = 0; + + while (task) + { + if (rtems_capture_task_valid (task)) + { + unsigned long long l = rtems_capture_task_delta_time (task); + + count++; + + total_time += l; + + for (i = 0; i < RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS; i++) + { + if (tasks[i]) + { + if ((l == 0) || (l < load[i])) + continue; + + for (j = (RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS - 1); j >= i; j--) + { + tasks[j + 1] = tasks[j]; + load[j + 1] = load[j]; + } + } + + tasks[i] = task; + load[i] = l; + break; + } + } + task = rtems_capture_next_task (task); + } + + fprintf (stdout, "\x1b[H\x1b[J Press ENTER to exit.\n\n"); + fprintf (stdout, + " PID NAME RPRI CPRI STATE %%CPU %%STK FLGS EXEC TIME\n"); + + if (count > last_count) + j = count; + else + j = last_count; + + for (i = 0; i < RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS; i++) + { + rtems_task_priority priority; + int stack_used; + int task_load; + int k; + + if (!tasks[i]) + break; + + j--; + + stack_used = rtems_capture_task_stack_usage (tasks[i]); + if (stack_used) + stack_used = (stack_used * 100) / stack_used; + + if (stack_used > 100) + stack_used = 100; + + task_load = (int) ((load[i] * 100000) / total_time); + + priority = rtems_capture_task_real_priority (tasks[i]); + + fprintf (stdout, "\x1b[K"); + rtems_monitor_dump_id (rtems_capture_task_id (tasks[i])); + fprintf (stdout, " "); + rtems_monitor_dump_name (rtems_capture_task_name (tasks[i])); + fprintf (stdout, " "); + rtems_monitor_dump_priority (priority); + fprintf (stdout, " "); + rtems_monitor_dump_priority (rtems_capture_task_curr_priority (tasks[i])); + fprintf (stdout, " "); + k = rtems_monitor_dump_state (rtems_capture_task_state (tasks[i])); + fprintf (stdout, "%*c %3i.%03i%% ", 6 - k, ' ', + task_load / 1000, task_load % 1000); + fprintf (stdout, "%3i%% %c%c", stack_used, + rtems_capture_task_valid (tasks[i]) ? 'a' : 'd', + rtems_capture_task_flags (tasks[i]) & RTEMS_CAPTURE_TRACED ? 't' : '-'); + + if ((floor > ceiling) && (ceiling > priority)) + fprintf (stdout, "--"); + else + fprintf (stdout, "%c%c", + rtems_capture_task_control (tasks[i]) ? + (rtems_capture_task_control_flags (tasks[i]) & + RTEMS_CAPTURE_WATCH ? 'w' : '+') : '-', + rtems_capture_watch_global_on () ? 'g' : '-'); + + fprintf (stdout, " %qi\n", rtems_capture_task_time (tasks[i])); + } + + if (count < RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS) + { + j = RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS - count; + while (j > 0) + { + fprintf (stdout, "\x1b[K\n"); + j--; + } + } + + last_count = count; + + cli_load_thread_active = 0; + + rtems_task_wake_after (RTEMS_MICROSECONDS_TO_TICKS (5000000)); + } +} + +/* + * rtems_capture_cli_task_load + * + * DESCRIPTION: + * + * This function is a monitor command. + * + */ + +static void +rtems_capture_cli_task_load (int argc __attribute__((unused)), + char** argv __attribute__((unused)), + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + rtems_task_priority priority; + rtems_name name; + rtems_id id; + + sc = rtems_task_set_priority (RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &priority); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: cannot obtain the current priority: %s\n", + rtems_status_text (sc)); + return; + } + + name = rtems_build_name('C', 'P', 'l', 't'); + + sc = rtems_task_create (name, priority, 4 * 1024, + RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL, + RTEMS_PREEMPT | RTEMS_TIMESLICE | RTEMS_NO_ASR, + &id); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: cannot create helper thread: %s\n", + rtems_status_text (sc)); + return; + } + + sc = rtems_task_start (id, rtems_capture_cli_task_load_thread, 0); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: cannot start helper thread: %s\n", + rtems_status_text (sc)); + rtems_task_delete (id); + return; + } + + for (;;) + { + int c = getchar (); + + if ((c == '\r') || (c == '\n')) + { + int loops = 20; + + while (loops && cli_load_thread_active) + rtems_task_wake_after (RTEMS_MICROSECONDS_TO_TICKS (100000)); + + rtems_task_delete (id); + + fprintf (stdout, "load monitoring stopped.\n"); + + return; + } + } +} + +/* + * rtems_capture_cli_watch_list + * + * DESCRIPTION: + * + * This function lists the controls in the capture engine. + * + */ + +static void +rtems_capture_cli_watch_list (int argc __attribute__((unused)), + char** argv __attribute__((unused)), + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_capture_control_t* control = rtems_capture_get_control_list (); + rtems_task_priority ceiling = rtems_capture_watch_get_ceiling (); + rtems_task_priority floor = rtems_capture_watch_get_floor (); + + fprintf (stdout, "watch priority ceiling is %" PRId32 "\n", ceiling); + fprintf (stdout, "watch priority floor is %" PRId32 "\n", floor); + fprintf (stdout, "global watch is %s\n", + rtems_capture_watch_global_on () ? "enabled" : "disabled"); + fprintf (stdout, "total %" PRId32 "\n", rtems_capture_control_count ()); + + while (control) + { + uint32_t flags; + int f; + int fshowed; + int lf; + + fprintf (stdout, " "); + rtems_monitor_dump_id (rtems_capture_control_id (control)); + fprintf (stdout, " "); + rtems_monitor_dump_name (rtems_capture_control_name (control)); + flags = rtems_capture_control_flags (control); + fprintf (stdout, " %c%c ", + rtems_capture_watch_global_on () ? 'g' : '-', + flags & RTEMS_CAPTURE_WATCH ? 'w' : '-'); + flags = rtems_capture_control_to_triggers (control); + fprintf (stdout, " T:%c%c%c%c%c%c%c", + flags & RTEMS_CAPTURE_SWITCH ? 'S' : '-', + flags & RTEMS_CAPTURE_CREATE ? 'C' : '-', + flags & RTEMS_CAPTURE_START ? 'S' : '-', + flags & RTEMS_CAPTURE_RESTART ? 'R' : '-', + flags & RTEMS_CAPTURE_DELETE ? 'D' : '-', + flags & RTEMS_CAPTURE_BEGIN ? 'B' : '-', + flags & RTEMS_CAPTURE_EXITTED ? 'E' : '-'); + flags = rtems_capture_control_from_triggers (control); + fprintf (stdout, " F:%c%c%c%c%c", + flags & RTEMS_CAPTURE_SWITCH ? 'S' : '-', + flags & RTEMS_CAPTURE_CREATE ? 'C' : '-', + flags & RTEMS_CAPTURE_START ? 'S' : '-', + flags & RTEMS_CAPTURE_RESTART ? 'R' : '-', + flags & RTEMS_CAPTURE_DELETE ? 'D' : '-'); + + for (f = 0, fshowed = 0, lf = 1; f < RTEMS_CAPTURE_TRIGGER_TASKS; f++) + { + if (rtems_capture_control_by_valid (control, f)) + { + if (lf && ((fshowed % 3) == 0)) + { + fprintf (stdout, "\n"); + lf = 0; + } + + fprintf (stdout, " %2i:", f); + rtems_monitor_dump_name (rtems_capture_control_by_name (control, f)); + fprintf (stdout, "/"); + rtems_monitor_dump_id (rtems_capture_control_by_id (control, f)); + flags = rtems_capture_control_by_triggers (control, f); + fprintf (stdout, ":%c%c%c%c%c", + flags & RTEMS_CAPTURE_SWITCH ? 'S' : '-', + flags & RTEMS_CAPTURE_CREATE ? 'C' : '-', + flags & RTEMS_CAPTURE_START ? 'S' : '-', + flags & RTEMS_CAPTURE_RESTART ? 'R' : '-', + flags & RTEMS_CAPTURE_DELETE ? 'D' : '-'); + fshowed++; + lf = 1; + } + } + + if (lf) + fprintf (stdout, "\n"); + + control = rtems_capture_next_control (control); + } +} + +/* + * rtems_capture_cli_get_name_id + * + * DESCRIPTION: + * + * This function checks arguments for a name or an id. + * + */ + +static bool +rtems_capture_cli_get_name_id (char* arg, + bool* valid_name, + bool* valid_id, + rtems_name* name, + rtems_id* id) +{ + size_t l; + size_t i; + + if (*valid_name && *valid_id) + { + fprintf (stdout, "error: too many arguments\n"); + return 0; + } + + /* + * See if the arg is all hex digits. + */ + + l = strlen (arg); + + for (i = 0; i < l; i++) + if (!isxdigit ((unsigned char)arg[i])) + break; + + if (i == l) + { + *id = strtoul (arg, 0, 16); + *valid_id = true; + } + else + { + /* + * This is a bit of hack but it should work on all platforms + * as it is what the score does with names. + * + * @warning The extra assigns play with the byte order so do not + * remove unless the score has been updated. + */ + rtems_name rname; + + rname = rtems_build_name(arg[0], arg[1], arg[2], arg[3]); + *name = rname; + *valid_name = true; + } + + return 1; +} + +/* + * rtems_capture_cli_watch_add + * + * DESCRIPTION: + * + * This function is a monitor command that add a watch to the capture + * engine. + * + */ + +static char const * watch_add_usage = "usage: cwadd [task name] [id]\n"; + +static void +rtems_capture_cli_watch_add (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + int arg; + rtems_name name = 0; + rtems_id id = 0; + bool valid_name = false; + bool valid_id = false; + + if (argc <= 1) + { + fprintf (stdout, watch_add_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + if (!rtems_capture_cli_get_name_id (argv[arg], &valid_name, &valid_id, + &name, &id)) + return; + } + } + + if (!valid_name && !valid_id) + { + fprintf (stdout, "error: no valid name or task id located\n"); + return; + } + + sc = rtems_capture_watch_add (name, id); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, + "error: watch add failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "watch added.\n"); +} + +/* + * rtems_capture_cli_watch_del + * + * DESCRIPTION: + * + * This function is a monitor command that deletes a watch from the capture + * engine. + * + */ + +static char const * watch_del_usage = "usage: cwdel [task name] [id]\n"; + +static void +rtems_capture_cli_watch_del (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + int arg; + rtems_name name = 0; + rtems_id id = 0; + bool valid_name = false; + bool valid_id = false; + + if (argc <= 1) + { + fprintf (stdout, watch_del_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + if (!rtems_capture_cli_get_name_id (argv[arg], &valid_name, &valid_id, + &name, &id)) + return; + } + } + + if (!valid_name && !valid_id) + { + fprintf (stdout, "error: no valid name or task id located\n"); + return; + } + + sc = rtems_capture_watch_del (name, id); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: watch delete failed: %s\n", + rtems_status_text (sc)); + return; + } + + fprintf (stdout, "watch delete.\n"); +} + +/* + * rtems_capture_cli_watch_control + * + * DESCRIPTION: + * + * This function is a monitor command that controls a watch. + * + */ + +static char const * watch_control_usage = "usage: cwctl [task name] [id] on/off\n"; + +static void +rtems_capture_cli_watch_control (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + int arg; + rtems_name name = 0; + rtems_id id = 0; + bool valid_name = false; + bool valid_id = false; + bool enable = false; + + if (argc <= 2) + { + fprintf (stdout, watch_control_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + if (strcmp (argv[arg], "on") == 0) + enable = true; + else if (strcmp (argv[arg], "off") == 0) + enable = false; + else if (!rtems_capture_cli_get_name_id (argv[arg], &valid_name, + &valid_id, &name, &id)) + return; + } + } + + if (!valid_name && !valid_id) + { + fprintf (stdout, "error: no valid name or task id located\n"); + return; + } + + sc = rtems_capture_watch_ctrl (name, id, enable); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: watch control failed: %s\n", + rtems_status_text (sc)); + return; + } + + fprintf (stdout, "watch %s.\n", enable ? "enabled" : "disabled"); +} + +/* + * rtems_capture_cli_watch_global + * + * DESCRIPTION: + * + * This function is a monitor command that sets a global watch. + * + */ + +static char const * watch_global_usage = "usage: cwglob on/off\n"; + +static void +rtems_capture_cli_watch_global (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + int arg; + bool enable = false; + + if (argc <= 1) + { + fprintf (stdout, watch_global_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + if (strcmp (argv[arg], "on") == 0) + enable = true; + else if (strcmp (argv[arg], "off") == 0) + enable = false; + } + } + + sc = rtems_capture_watch_global (enable); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: global watch failed: %s\n", + rtems_status_text (sc)); + return; + } + + fprintf (stdout, "global watch %s.\n", enable ? "enabled" : "disabled"); +} + +/* + * rtems_capture_cli_watch_ceiling + * + * DESCRIPTION: + * + * This function is a monitor command that sets watch ceiling. + * + */ + +static char const * watch_ceiling_usage = "usage: cwceil priority\n"; + +static void +rtems_capture_cli_watch_ceiling (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + int arg; + rtems_task_priority priority = 0; + + if (argc <= 1) + { + fprintf (stdout, watch_ceiling_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + priority = strtoul (argv[arg], 0, 0); + } + } + + sc = rtems_capture_watch_ceiling (priority); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: watch ceiling failed: %s\n", + rtems_status_text (sc)); + return; + } + + fprintf (stdout, "watch ceiling is %" PRId32 ".\n", priority); +} + +/* + * rtems_capture_cli_watch_floor + * + * DESCRIPTION: + * + * This function is a monitor command that sets watch floor. + * + */ + +static char const * watch_floor_usage = "usage: cwfloor priority\n"; + +static void +rtems_capture_cli_watch_floor (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + int arg; + rtems_task_priority priority = 0; + + if (argc <= 1) + { + fprintf (stdout, watch_floor_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + priority = strtoul (argv[arg], 0, 0); + } + } + + sc = rtems_capture_watch_floor (priority); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: watch floor failed: %s\n", + rtems_status_text (sc)); + return; + } + + fprintf (stdout, "watch floor is %" PRId32 ".\n", priority); +} + +/* + * rtems_capture_cli_trigger_worker + * + * DESCRIPTION: + * + * This function is a monitor command that sets or clears a trigger. + * + */ + +static char const *trigger_set_usage = + "usage: %s [-?] type [to name/id] [from] [from name/id]\n"; + +static char const *trigger_set_types = + " You can say 'type TASK' or 'type TO from FROM'\n" \ + " where TASK is the task the event is happening to\n" \ + " or you can say the event TO this task FROM this task.\n" \ + " No type defaults to 'switch'.\n" \ + " switch : context switch TASK or FROM or FROM->TO\n" \ + " create : create TASK, or create TO from FROM\n" \ + " start : start TASK, or start TO from FROM\n" \ + " restart : restart TASK, or restart TO from FROM\n" \ + " delete : delete TASK or delete TO from FROM\n" \ + " begin : begin TASK\n" \ + " exitted : exitted TASK\n"; + +/* + * Structure to handle the parsing of the trigger command line. + */ +typedef struct rtems_capture_cli_triggers_s +{ + char const * name; + rtems_capture_trigger_t type; + int to_only; +} rtems_capture_cli_triggers_t; + +static rtems_capture_cli_triggers_t rtems_capture_cli_triggers[] = +{ + { "switch", rtems_capture_switch, 0 }, /* must be first */ + { "create", rtems_capture_create, 0 }, + { "start", rtems_capture_start, 0 }, + { "restart", rtems_capture_restart, 0 }, + { "delete", rtems_capture_delete, 0 }, + { "begin", rtems_capture_begin, 1 }, + { "exitted", rtems_capture_exitted, 1 } +}; + +typedef enum rtems_capture_cli_trig_state_e +{ + trig_type, + trig_to, + trig_from_from, + trig_from +} rtems_capture_cli_trig_state_t; + +#define RTEMS_CAPTURE_CLI_TRIGGERS_NUM \ + (sizeof (rtems_capture_cli_triggers) / sizeof (rtems_capture_cli_triggers_t)) + +static void +rtems_capture_cli_trigger_worker (int set, int argc, char** argv) +{ + rtems_status_code sc; + int arg; + int trigger = 0; /* switch */ + rtems_capture_trigger_mode_t trigger_mode = rtems_capture_from_any; + bool trigger_set = false; + bool is_from = false; + rtems_name name = 0; + rtems_id id = 0; + bool valid_name = false; + bool valid_id = false; + rtems_name from_name = 0; + rtems_id from_id = 0; + bool from_valid_name = false; + bool from_valid_id = false; + rtems_name to_name = 0; + rtems_id to_id = 0; + bool to_valid_name = false; + bool to_valid_id = false; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + switch (argv[arg][1]) + { + case '?': + fprintf (stdout, trigger_set_usage, set ? "ctset" : "ctclear"); + fprintf (stdout, trigger_set_types); + return; + default: + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + break; + } + } + else + { + if (!trigger_set) + { + bool found = false; + int t; + + for (t = 0; t < RTEMS_CAPTURE_CLI_TRIGGERS_NUM; t++) + if (strcmp (argv[arg], rtems_capture_cli_triggers[t].name) == 0) + { + trigger = t; + found = true; + break; + } + + trigger_set = true; + + /* + * If a trigger was not found assume the default and + * assume the parameter is a task name or id. + */ + if (found) + continue; + } + + if (strcmp (arg[argv], "from") == 0) + { + if (is_from) + fprintf (stdout, "warning: extra 'from' ignored\n"); + + is_from = 1; + continue; + } + + if (!rtems_capture_cli_get_name_id (argv[arg], &valid_name, &valid_id, + &name, &id)) + return; + + if (valid_name) + { + if (is_from) + { + if (!from_valid_name && !from_valid_id) + { + from_valid_name = true; + from_name = name; + } + else + fprintf (stdout, "warning: extra arguments ignored\n"); + } + else if (!to_valid_name && !to_valid_id) + { + to_valid_name = true; + to_name = name; + } + else + fprintf (stdout, "warning: extra arguments ignored\n"); + } + + if (valid_id) + { + if (is_from) + { + if (!from_valid_name && !from_valid_id) + { + from_valid_id = true; + from_id = id; + } + else + fprintf (stdout, "warning: extra arguments ignored\n"); + } + else if (!to_valid_name && !to_valid_id) + { + to_valid_id = true; + to_id = id; + } + else + fprintf (stdout, "warning: extra arguments ignored\n"); + } + } + } + + if (is_from && rtems_capture_cli_triggers[trigger].to_only) + { + fprintf (stdout, "error: a %s trigger can be a TO trigger\n", + rtems_capture_cli_triggers[trigger].name); + return; + } + + if (!to_valid_name && !to_valid_id && !from_valid_name && !from_valid_id) + { + fprintf (stdout, trigger_set_usage); + return; + } + + if (!is_from && !to_valid_name && !to_valid_id) + { + fprintf (stdout, "error: a %s trigger needs a TO name or id\n", + rtems_capture_cli_triggers[trigger].name); + return; + } + + if (is_from && !from_valid_name && !from_valid_id) + { + fprintf (stdout, "error: a %s trigger needs a FROM name or id\n", + rtems_capture_cli_triggers[trigger].name); + return; + } + + if ((from_valid_name || from_valid_id) && (to_valid_name || to_valid_id)) + trigger_mode = rtems_capture_from_to; + else if (from_valid_name || from_valid_id) + trigger_mode = rtems_capture_to_any; + else if (to_valid_name || to_valid_id) + trigger_mode = rtems_capture_from_any; + + if (set) + sc = rtems_capture_set_trigger (from_name, from_id, to_name, to_id, + trigger_mode, + rtems_capture_cli_triggers[trigger].type); + else + sc = rtems_capture_clear_trigger (from_name, from_id, to_name, to_id, + trigger_mode, + rtems_capture_cli_triggers[trigger].type); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: %sing the trigger failed: %s\n", + set ? "sett" : "clear", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "trigger %s.\n", set ? "set" : "cleared"); +} + +/* + * rtems_capture_cli_trigger_set + * + * DESCRIPTION: + * + * This function is a monitor command that sets a trigger. + * + */ + +static void +rtems_capture_cli_trigger_set (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_capture_cli_trigger_worker (1, argc, argv); +} + +/* + * rtems_capture_cli_trigger_clear + * + * DESCRIPTION: + * + * This function is a monitor command that clears a trigger. + * + */ + +static void +rtems_capture_cli_trigger_clear (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_capture_cli_trigger_worker (0, argc, argv); +} + +/* + * rtems_capture_cli_trace_records + * + * DESCRIPTION: + * + * This function is a monitor command that dumps trace records. + * + */ + +static void +rtems_capture_cli_trace_records (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + bool csv = false; + static int dump_total = 22; + int total; + int count; + uint32_t read; + rtems_capture_record_t* rec; + int arg; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + if (argv[arg][1] == 'c') + csv = true; + else + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + size_t i; + size_t l; + + l = strlen (argv[arg]); + + for (i = 0; i < l; i++) + if (!isdigit ((unsigned char)argv[arg][i])) + { + fprintf (stdout, "error: not a number\n"); + return; + } + + dump_total = strtoul (argv[arg], 0, 0); + } + } + + total = dump_total; + + while (total) + { + sc = rtems_capture_read (0, 0, &read, &rec); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: trace read failed: %s\n", rtems_status_text (sc)); + rtems_capture_flush (0); + return; + } + + /* + * If we have no records then just exist. We still need to release + * the reader lock. + */ + + if (read == 0) + { + rtems_capture_release (read); + break; + } + + count = total < read ? total : read; + + while (count--) + { + if (csv) + fprintf (stdout, "%08" PRIxPTR ",%03" PRIu32 + ",%03" PRIu32 ",%04" PRIx32 ",%" PRId32 ",%" PRId32 "\n", + (uintptr_t) rec->task, + (rec->events >> RTEMS_CAPTURE_REAL_PRIORITY_EVENT) & 0xff, + (rec->events >> RTEMS_CAPTURE_CURR_PRIORITY_EVENT) & 0xff, + (rec->events >> RTEMS_CAPTURE_EVENT_START), + rec->ticks, rec->tick_offset); + else + { + unsigned long long t; + uint32_t event; + int e; + + event = rec->events >> RTEMS_CAPTURE_EVENT_START; + + t = rec->ticks; + t *= rtems_capture_tick_time (); + t += rec->tick_offset; + + for (e = RTEMS_CAPTURE_EVENT_START; e < RTEMS_CAPTURE_EVENT_END; e++) + { + if (event & 1) + { + fprintf (stdout, "%9li.%06li ", (unsigned long) (t / 1000000), + (unsigned long) (t % 1000000)); + rtems_monitor_dump_id (rtems_capture_task_id (rec->task)); + fprintf (stdout, " "); + rtems_monitor_dump_name (rtems_capture_task_name (rec->task)); + fprintf (stdout, " %3" PRId32 " %3" PRId32 " %s\n", + (rec->events >> RTEMS_CAPTURE_REAL_PRIORITY_EVENT) & 0xff, + (rec->events >> RTEMS_CAPTURE_CURR_PRIORITY_EVENT) & 0xff, + rtems_capture_event_text (e)); + } + event >>= 1; + } + } + rec++; + } + + count = total < read ? total : read; + + if (count < total) + total -= count; + else + total = 0; + + rtems_capture_release (count); + } +} + +/* + * rtems_capture_cli_flush + * + * DESCRIPTION: + * + * This function is a monitor command that flushes and primes the capture + * engine. + * + */ + +static void +rtems_capture_cli_flush (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + bool prime = true; + int arg; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + if (argv[arg][1] == 'n') + prime = false; + else + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + } + + sc = rtems_capture_flush (prime); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: flush failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "trace buffer flushed and %s.\n", + prime ? "primed" : "not primed"); +} + +static rtems_monitor_command_entry_t rtems_capture_cli_cmds[] = +{ + { + "copen", + "usage: copen [-i] size\n", + 0, + rtems_capture_cli_open, + { 0 }, + 0 + }, + { + "cclose", + "usage: cclose\n", + 0, + rtems_capture_cli_close, + { 0 }, + 0 + }, + { + "cenable", + "usage: cenable\n", + 0, + rtems_capture_cli_enable, + { 0 }, + 0 + }, + { + "cdisable", + "usage: cdisable\n", + 0, + rtems_capture_cli_disable, + { 0 }, + 0 + }, + { + "ctlist", + "usage: ctlist \n", + 0, + rtems_capture_cli_task_list, + { 0 }, + 0 + }, + { + "ctload", + "usage: ctload \n", + 0, + rtems_capture_cli_task_load, + { 0 }, + 0 + }, + { + "cwlist", + "usage: cwlist\n", + 0, + rtems_capture_cli_watch_list, + { 0 }, + 0 + }, + { + "cwadd", + "usage: cwadd [task name] [id]\n", + 0, + rtems_capture_cli_watch_add, + { 0 }, + 0 + }, + { + "cwdel", + "usage: cwdel [task name] [id]\n", + 0, + rtems_capture_cli_watch_del, + { 0 }, + 0 + }, + { + "cwctl", + "usage: cwctl [task name] [id] on/off\n", + 0, + rtems_capture_cli_watch_control, + { 0 }, + 0 + }, + { + "cwglob", + "usage: cwglob on/off\n", + 0, + rtems_capture_cli_watch_global, + { 0 }, + 0 + }, + { + "cwceil", + "usage: cwceil priority\n", + 0, + rtems_capture_cli_watch_ceiling, + { 0 }, + 0 + }, + { + "cwfloor", + "usage: cwfloor priority\n", + 0, + rtems_capture_cli_watch_floor, + { 0 }, + 0 + }, + { + "ctrace", + "usage: ctrace [-c] [-r records]\n", + 0, + rtems_capture_cli_trace_records, + { 0 }, + 0 + }, + { + "ctset", + "usage: ctset -h\n", + 0, + rtems_capture_cli_trigger_set, + { 0 }, + 0 + }, + { + "ctclear", + "usage: ctclear -?\n", + 0, + rtems_capture_cli_trigger_clear, + { 0 }, + 0 + }, + { + "cflush", + "usage: cflush [-n]\n", + 0, + rtems_capture_cli_flush, + { 0 }, + 0 + } +}; + +/* + * rtems_capture_cli_init + * + * DESCRIPTION: + * + * This function initialises the command line interface to the capture + * engine. + * + */ + +rtems_status_code +rtems_capture_cli_init (rtems_capture_timestamp timestamp) +{ + size_t cmd; + + capture_timestamp = timestamp; + + for (cmd = 0; + cmd < sizeof (rtems_capture_cli_cmds) / sizeof (rtems_monitor_command_entry_t); + cmd++) + rtems_monitor_insert_cmd (&rtems_capture_cli_cmds[cmd]); + + return RTEMS_SUCCESSFUL; +} diff --git a/cpukit/libmisc/capture/capture-cli.h b/cpukit/libmisc/capture/capture-cli.h new file mode 100644 index 0000000000..99c4e2252c --- /dev/null +++ b/cpukit/libmisc/capture/capture-cli.h @@ -0,0 +1,56 @@ +/** + * @file rtems/capture-cli.h + */ + +/* + ------------------------------------------------------------------------ + $Id$ + ------------------------------------------------------------------------ + + Copyright Objective Design Systems Pty Ltd, 2002 + All rights reserved Objective Design Systems Pty Ltd, 2002 + Chris Johns (ccj@acm.org) + + COPYRIGHT (c) 1989-1998. + On-Line Applications Research Corporation (OAR). + + The license and distribution terms for this file may be + found in the file LICENSE in this distribution. + + This software with is provided ``as is'' and with NO WARRANTY. + + ------------------------------------------------------------------------ + + RTEMS Performance Monitoring and Measurement Framework. + + This is the Target Interface Command Line Interface. You need + start the RTEMS monitor. + +*/ + +#ifndef __CAPTURE_CLI_H_ +#define __CAPTURE_CLI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rtems/capture.h> + +/** + * rtems_capture_cli_init + * + * DESCRIPTION: + * + * This function initialises the command line interface to the capture + * engine. + * + */ +rtems_status_code +rtems_capture_cli_init (rtems_capture_timestamp timestamp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cpukit/libmisc/capture/capture.c b/cpukit/libmisc/capture/capture.c new file mode 100644 index 0000000000..ac1acc01ac --- /dev/null +++ b/cpukit/libmisc/capture/capture.c @@ -0,0 +1,1938 @@ +/* + ------------------------------------------------------------------------ + $Id$ + ------------------------------------------------------------------------ + + Copyright Objective Design Systems Pty Ltd, 2002 + All rights reserved Objective Design Systems Pty Ltd, 2002 + Chris Johns (ccj@acm.org) + + COPYRIGHT (c) 1989-2009. + On-Line Applications Research Corporation (OAR). + + The license and distribution terms for this file may be + found in the file LICENSE in this distribution. + + This software with is provided ``as is'' and with NO WARRANTY. + + ------------------------------------------------------------------------ + + RTEMS Performance Monitoring and Measurement Framework. + + This is the Capture Engine component. + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "capture.h" +#include <rtems/score/states.inl> +#include <rtems/score/wkspace.h> +#include <rtems/score/wkspace.inl> + +/* + * These events are always recorded and are not part of the + * watch filters. + * + * This feature has been disabled as it becomes confusing when + * setting up filters and some event leak. + */ +#if defined (RTEMS_CAPTURE_ENGINE_ALLOW_RELATED_EVENTS) +#define RTEMS_CAPTURE_RECORD_EVENTS (RTEMS_CAPTURE_CREATED_BY_EVENT | \ + RTEMS_CAPTURE_CREATED_EVENT | \ + RTEMS_CAPTURE_STARTED_BY_EVENT | \ + RTEMS_CAPTURE_STARTED_EVENT | \ + RTEMS_CAPTURE_RESTARTED_BY_EVENT | \ + RTEMS_CAPTURE_RESTARTED_EVENT | \ + RTEMS_CAPTURE_DELETED_BY_EVENT | \ + RTEMS_CAPTURE_DELETED_EVENT | \ + RTEMS_CAPTURE_BEGIN_EVENT | \ + RTEMS_CAPTURE_EXITTED_EVENT) +#else +#define RTEMS_CAPTURE_RECORD_EVENTS (0) +#endif + +/* + * Global capture flags. + */ +#define RTEMS_CAPTURE_ON (1 << 0) +#define RTEMS_CAPTURE_NO_MEMORY (1 << 1) +#define RTEMS_CAPTURE_OVERFLOW (1 << 2) +#define RTEMS_CAPTURE_TRIGGERED (1 << 3) +#define RTEMS_CAPTURE_READER_ACTIVE (1 << 4) +#define RTEMS_CAPTURE_READER_WAITING (1 << 5) +#define RTEMS_CAPTURE_GLOBAL_WATCH (1 << 6) +#define RTEMS_CAPTURE_ONLY_MONITOR (1 << 7) + +/* + * RTEMS Capture Data. + */ +static rtems_capture_record_t* capture_records; +static uint32_t capture_size; +static uint32_t capture_count; +static rtems_capture_record_t* capture_in; +static uint32_t capture_out; +static uint32_t capture_flags; +static rtems_capture_task_t* capture_tasks; +static rtems_capture_control_t* capture_controls; +static int capture_extension_index; +static rtems_id capture_id; +static rtems_capture_timestamp capture_timestamp; +static rtems_task_priority capture_ceiling; +static rtems_task_priority capture_floor; +static uint32_t capture_tick_period; +static rtems_id capture_reader; + +/* + * RTEMS Event text. + */ +static const char* capture_event_text[] = +{ + "CREATED_BY", + "CREATED", + "STARTED_BY", + "STARTED", + "RESTARTED_BY", + "RESTARTED", + "DELETED_BY", + "DELETED", + "BEGIN", + "EXITTED", + "SWITCHED_OUT", + "SWITCHED_IN", + "TIMESTAMP" +}; + +/* + * rtems_capture_get_time + * + * DESCRIPTION: + * + * This function returns the current time. If a handler is provided + * by the user get the time from that. + */ +static inline void rtems_capture_get_time (uint32_t* ticks, + uint32_t* tick_offset) +{ + if (capture_timestamp) + capture_timestamp (ticks, tick_offset); + else + { + *ticks = _Watchdog_Ticks_since_boot; + *tick_offset = 0; + } +} + +/* + * rtems_capture_match_names + * + * DESCRIPTION: + * + * This function compares rtems_names. It protects the + * capture engine from a change to the way names are supported + * in RTEMS. + * + */ +static inline bool +rtems_capture_match_names (rtems_name lhs, rtems_name rhs) +{ + return lhs == rhs; +} + +/* + * rtems_capture_match_id + * + * DESCRIPTION: + * + * This function compares rtems_ids. It protects the + * capture engine from a change to the way id are supported + * in RTEMS. + * + */ +static inline bool +rtems_capture_match_ids (rtems_id lhs, rtems_id rhs) +{ + return lhs == rhs; +} + +/* + * rtems_capture_match_name_id + * + * DESCRIPTION: + * + * This function matches a name and/or id. + */ +static inline bool +rtems_capture_match_name_id (rtems_name lhs_name, + rtems_id lhs_id, + rtems_name rhs_name, + rtems_id rhs_id) +{ + /* + * The left hand side name or id could be 0 which means a wildcard. + */ + if ((lhs_name == 0) && (lhs_id == rhs_id)) + return 1; + else if ((lhs_id == 0) || (lhs_id == rhs_id)) + { + if (rtems_capture_match_names (lhs_name, rhs_name)) + return 1; + } + return 0; +} + +/* + * rtems_capture_dup_name + * + * DESCRIPTION: + * + * This function duplicates an rtems_names. It protects the + * capture engine from a change to the way names are supported + * in RTEMS. + * + */ +static inline void +rtems_capture_dup_name (rtems_name* dst, rtems_name src) +{ + *dst = src; +} + +/* + * rtems_capture_by_in_to + * + * DESCRIPTION: + * + * This function sees if a BY control is in the BY names. The use + * of the valid_mask in this way assumes the number of trigger + * tasks is the number of bits in uint32_t. + * + */ +static inline bool +rtems_capture_by_in_to (uint32_t events, + rtems_capture_task_t* by, + rtems_capture_control_t* to) +{ + uint32_t valid_mask = RTEMS_CAPTURE_CONTROL_FROM_MASK (0); + uint32_t valid_remainder = 0xffffffff; + int i; + + for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++) + { + /* + * If there are no more valid BY entries then + * we are finished. + */ + if ((valid_remainder & to->by_valid) == 0) + break; + + /* + * Is the froby entry valid and does its name or id match. + */ + if ((valid_mask & to->by_valid) && + (to->by[i].trigger & events)) + { + /* + * We have the BY task on the right hand side so we + * match with id's first then labels if the id's are + * not set. + */ + if (rtems_capture_match_name_id (to->by[i].name, to->by[i].id, + by->name, by->id)) + return 1; + } + + valid_mask >>= 1; + valid_remainder >>= 1; + } + + return 0; +} + +/* + * rtems_capture_refcount_up + * + * DESCRIPTION: + * + * This function raises the reference count. + * + */ +static inline void +rtems_capture_refcount_up (rtems_capture_task_t* task) +{ + task->refcount++; +} + +/* + * rtems_capture_refcount_down + * + * DESCRIPTION: + * + * This function lowers the reference count and if the count + * reaches 0 the task control block is returned to the heap. + * + */ +static inline void +rtems_capture_refcount_down (rtems_capture_task_t* task) +{ + if (task->refcount) + task->refcount--; +} + +/* + * rtems_capture_init_stack_usage + * + * DESCRIPTION: + * + * This function setups a stack so its usage can be monitored. + */ +static inline void +rtems_capture_init_stack_usage (rtems_capture_task_t* task) +{ + if (task->tcb) + { + uint32_t* s; + uint32_t i; + + task->stack_size = task->tcb->Start.Initial_stack.size; + task->stack_clean = task->stack_size; + + s = task->tcb->Start.Initial_stack.area; + + for (i = 0; i < (task->stack_size - 128); i += 4) + *(s++) = 0xdeaddead; + } +} + +/* + * rtems_capture_find_control + * + * DESCRIPTION: + * + * This function searches for a trigger given a name. + * + */ +static inline rtems_capture_control_t* +rtems_capture_find_control (rtems_name name, rtems_id id) +{ + rtems_capture_control_t* control; + + for (control = capture_controls; control != NULL; control = control->next) + if (rtems_capture_match_name_id (name, id, control->name, control->id)) + break; + return control; +} + +/* + * rtems_capture_create_control + * + * DESCRIPTION: + * + * This function creates a capture control for the capture engine. + * + */ +static inline rtems_capture_control_t* +rtems_capture_create_control (rtems_name name, rtems_id id) +{ + rtems_interrupt_level level; + rtems_capture_control_t* control; + rtems_capture_task_t* task; + + if ((name == 0) && (id == 0)) + return NULL; + + control = rtems_capture_find_control (name, id); + + if (control == NULL) + { + control = _Workspace_Allocate (sizeof (rtems_capture_control_t)); + + if (control == NULL) + { + capture_flags |= RTEMS_CAPTURE_NO_MEMORY; + return NULL; + } + + control->name = name; + control->id = id; + control->flags = 0; + control->to_triggers = 0; + control->from_triggers = 0; + control->by_valid = 0; + + memset (control->by, 0, sizeof (control->by)); + + rtems_interrupt_disable (level); + + control->next = capture_controls; + capture_controls = control; + + /* + * We need to scan the task list as set the control to the + * tasks. + */ + for (task = capture_tasks; task != NULL; task = task->forw) + if (rtems_capture_match_name_id (name, id, task->name, task->id)) + task->control = control; + + rtems_interrupt_enable (level); + } + + return control; +} + +/* + * rtems_capture_create_capture_task + * + * DESCRIPTION: + * + * This function create the task control. + * + */ +static inline rtems_capture_task_t* +rtems_capture_create_capture_task (rtems_tcb* new_task) +{ + rtems_interrupt_level level; + rtems_capture_task_t* task; + rtems_capture_control_t* control; + rtems_name name; + + task = _Workspace_Allocate (sizeof (rtems_capture_task_t)); + + if (task == NULL) + { + capture_flags |= RTEMS_CAPTURE_NO_MEMORY; + return NULL; + } + + /* + * Check the type of name the object has. + */ + + rtems_object_get_classic_name( new_task->Object.id, &name ); + + rtems_capture_dup_name (&task->name, name); + + task->id = new_task->Object.id; + task->flags = 0; + task->in = 0; + task->refcount = 0; + task->out = 0; + task->tcb = new_task; + task->ticks = 0; + task->tick_offset = 0; + task->ticks_in = 0; + task->tick_offset_in = 0; + task->control = 0; + task->last_ticks = 0; + task->last_tick_offset = 0; + + task->tcb->extensions[capture_extension_index] = task; + + task->start_priority = new_task->Start.initial_priority; + task->stack_size = new_task->Start.Initial_stack.size; + task->stack_clean = task->stack_size; + + rtems_interrupt_disable (level); + + task->forw = capture_tasks; + if (task->forw) + task->forw->back = task; + task->back = NULL; + capture_tasks = task; + + rtems_interrupt_enable (level); + + /* + * We need to scan the default control list to initialise + * this control. + */ + + for (control = capture_controls; control != NULL; control = control->next) + if (rtems_capture_match_name_id (control->name, control->id, + task->name, task->id)) + task->control = control; + + return task; +} + +/* + * rtems_capture_destroy_capture_task + * + * DESCRIPTION: + * + * This function destroy the task structure if the reference count + * is 0 and the tcb has been cleared signalling the task has been + * deleted. + * + */ +static inline void +rtems_capture_destroy_capture_task (rtems_capture_task_t* task) +{ + if (task) + { + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + if (task->tcb || task->refcount) + task = 0; + + if (task) + { + if (task->forw) + task->forw->back = task->back; + if (task->back) + task->back->forw = task->forw; + else + capture_tasks = task->forw; + } + + rtems_interrupt_enable (level); + + _Workspace_Free (task); + } +} + +/* + * rtems_capture_record + * + * DESCRIPTION: + * + * This function records a capture record into the capture buffer. + * + */ +static inline void +rtems_capture_record (rtems_capture_task_t* task, + uint32_t events) +{ + /* + * Check the watch state if we have a task control, and + * the task's real priority is lower or equal to the ceiling. + */ + if (task && + ((capture_flags & + (RTEMS_CAPTURE_TRIGGERED | RTEMS_CAPTURE_ONLY_MONITOR)) == + RTEMS_CAPTURE_TRIGGERED)) + { + rtems_capture_control_t* control; + + control = task->control; + + /* + * Capure the record if we have an event that is always + * captured, or the task's real priority is greater than the + * watch ceiling, and the global watch or task watch is enabled. + */ + + if ((events & RTEMS_CAPTURE_RECORD_EVENTS) || + ((task->tcb->real_priority >= capture_ceiling) && + (task->tcb->real_priority <= capture_floor) && + ((capture_flags & RTEMS_CAPTURE_GLOBAL_WATCH) || + (control && (control->flags & RTEMS_CAPTURE_WATCH))))) + { + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + if (capture_count < capture_size) + { + capture_count++; + capture_in->task = task; + capture_in->events = (events | + (task->tcb->real_priority) | + (task->tcb->current_priority << 8)); + + if ((events & RTEMS_CAPTURE_RECORD_EVENTS) == 0) + task->flags |= RTEMS_CAPTURE_TRACED; + + rtems_capture_get_time (&capture_in->ticks, &capture_in->tick_offset); + + if (capture_in == &capture_records[capture_size - 1]) + capture_in = capture_records; + else + capture_in++; + + rtems_capture_refcount_up (task); + } + else + capture_flags |= RTEMS_CAPTURE_OVERFLOW; + rtems_interrupt_enable (level); + } + } +} + +/* + * rtems_capture_trigger + * + * DESCRIPTION: + * + * See if we have triggered and if not see if this event is a + * cause of a trigger. + */ +bool +rtems_capture_trigger (rtems_capture_task_t* ft, + rtems_capture_task_t* tt, + uint32_t events) +{ + /* + * If we have not triggered then see if this is a trigger condition. + */ + if (!(capture_flags & RTEMS_CAPTURE_TRIGGERED)) + { + rtems_capture_control_t* fc = NULL; + rtems_capture_control_t* tc = NULL; + uint32_t from_events = 0; + uint32_t to_events = 0; + uint32_t from_to_events = 0; + + if (ft) + { + fc = ft->control; + if (fc) + from_events = fc->from_triggers & events; + } + + if (tt) + { + tc = tt->control; + if (tc) + { + to_events = tc->to_triggers & events; + if (ft && tc->by_valid) + from_to_events = tc->by_triggers & events; + } + } + + /* + * Check if we have any from or to events. These are the + * from any or to any type triggers. All from/to triggers are + * listed in the to's control with the from in the from list. + * + * The masking above means any flag set is a trigger. + */ + if (from_events || to_events) + { + capture_flags |= RTEMS_CAPTURE_TRIGGERED; + return 1; + } + + /* + * Check the from->to events. + */ + if (from_to_events) + { + if (rtems_capture_by_in_to (events, ft, tc)) + { + capture_flags |= RTEMS_CAPTURE_TRIGGERED; + return 1; + } + } + + return 0; + } + + return 1; +} + +/* + * rtems_capture_create_task + * + * DESCRIPTION: + * + * This function is called when a task is created. + * + */ +static bool +rtems_capture_create_task (rtems_tcb* current_task, + rtems_tcb* new_task) +{ + rtems_capture_task_t* ct; + rtems_capture_task_t* nt; + + ct = current_task->extensions[capture_extension_index]; + + /* + * The task pointers may not be known as the task may have + * been created before the capture engine was open. Add them. + */ + + if (ct == NULL) + ct = rtems_capture_create_capture_task (current_task); + + /* + * Create the new task's capture control block. + */ + nt = rtems_capture_create_capture_task (new_task); + + if (rtems_capture_trigger (ct, nt, RTEMS_CAPTURE_CREATE)) + { + rtems_capture_record (ct, RTEMS_CAPTURE_CREATED_BY_EVENT); + rtems_capture_record (nt, RTEMS_CAPTURE_CREATED_EVENT); + } + + return 1 == 1; +} + +/* + * rtems_capture_start_task + * + * DESCRIPTION: + * + * This function is called when a task is started. + * + */ +static void +rtems_capture_start_task (rtems_tcb* current_task, + rtems_tcb* started_task) +{ + /* + * Get the capture task control block so we can trace this + * event. + */ + rtems_capture_task_t* ct; + rtems_capture_task_t* st; + + ct = current_task->extensions[capture_extension_index]; + st = started_task->extensions[capture_extension_index]; + + /* + * The task pointers may not be known as the task may have + * been created before the capture engine was open. Add them. + */ + + if (ct == NULL) + ct = rtems_capture_create_capture_task (current_task); + + if (st == NULL) + st = rtems_capture_create_capture_task (started_task); + + if (rtems_capture_trigger (ct, st, RTEMS_CAPTURE_START)) + { + rtems_capture_record (ct, RTEMS_CAPTURE_STARTED_BY_EVENT); + rtems_capture_record (st, RTEMS_CAPTURE_STARTED_EVENT); + } + + rtems_capture_init_stack_usage (st); +} + +/* + * rtems_capture_restart_task + * + * DESCRIPTION: + * + * This function is called when a task is restarted. + * + */ +static void +rtems_capture_restart_task (rtems_tcb* current_task, + rtems_tcb* restarted_task) +{ + /* + * Get the capture task control block so we can trace this + * event. + */ + rtems_capture_task_t* ct; + rtems_capture_task_t* rt; + + ct = current_task->extensions[capture_extension_index]; + rt = restarted_task->extensions[capture_extension_index]; + + /* + * The task pointers may not be known as the task may have + * been created before the capture engine was open. Add them. + */ + + if (ct == NULL) + ct = rtems_capture_create_capture_task (current_task); + + if (rt == NULL) + rt = rtems_capture_create_capture_task (restarted_task); + + if (rtems_capture_trigger (ct, rt, RTEMS_CAPTURE_RESTART)) + { + rtems_capture_record (ct, RTEMS_CAPTURE_RESTARTED_BY_EVENT); + rtems_capture_record (rt, RTEMS_CAPTURE_RESTARTED_EVENT); + } + + rtems_capture_task_stack_usage (rt); + rtems_capture_init_stack_usage (rt); +} + +/* + * rtems_capture_delete_task + * + * DESCRIPTION: + * + * This function is called when a task is deleted. + * + */ +static void +rtems_capture_delete_task (rtems_tcb* current_task, + rtems_tcb* deleted_task) +{ + /* + * Get the capture task control block so we can trace this + * event. + */ + rtems_capture_task_t* ct; + rtems_capture_task_t* dt; + + /* + * The task pointers may not be known as the task may have + * been created before the capture engine was open. Add them. + */ + + ct = current_task->extensions[capture_extension_index]; + dt = deleted_task->extensions[capture_extension_index]; + + if (ct == NULL) + ct = rtems_capture_create_capture_task (current_task); + + if (dt == NULL) + dt = rtems_capture_create_capture_task (deleted_task); + + if (rtems_capture_trigger (ct, dt, RTEMS_CAPTURE_DELETE)) + { + rtems_capture_record (ct, RTEMS_CAPTURE_DELETED_BY_EVENT); + rtems_capture_record (dt, RTEMS_CAPTURE_DELETED_EVENT); + } + + rtems_capture_task_stack_usage (dt); + + /* + * This task's tcb will be invalid. This signals the + * task has been deleted. + */ + dt->tcb = 0; + + rtems_capture_destroy_capture_task (dt); +} + +/* + * rtems_capture_begin_task + * + * DESCRIPTION: + * + * This function is called when a task is begun. + * + */ +static void +rtems_capture_begin_task (rtems_tcb* begin_task) +{ + /* + * Get the capture task control block so we can trace this + * event. + */ + rtems_capture_task_t* bt; + + bt = begin_task->extensions[capture_extension_index]; + + /* + * The task pointers may not be known as the task may have + * been created before the capture engine was open. Add them. + */ + + if (bt == NULL) + bt = rtems_capture_create_capture_task (begin_task); + + if (rtems_capture_trigger (NULL, bt, RTEMS_CAPTURE_BEGIN)) + rtems_capture_record (bt, RTEMS_CAPTURE_BEGIN_EVENT); +} + +/* + * rtems_capture_exitted_task + * + * DESCRIPTION: + * + * This function is called when a task is exitted. That is + * returned rather than was deleted. + * + */ +static void +rtems_capture_exitted_task (rtems_tcb* exitted_task) +{ + /* + * Get the capture task control block so we can trace this + * event. + */ + rtems_capture_task_t* et; + + et = exitted_task->extensions[capture_extension_index]; + + /* + * The task pointers may not be known as the task may have + * been created before the capture engine was open. Add them. + */ + + if (et == NULL) + et = rtems_capture_create_capture_task (exitted_task); + + if (rtems_capture_trigger (NULL, et, RTEMS_CAPTURE_EXITTED)) + rtems_capture_record (et, RTEMS_CAPTURE_EXITTED_EVENT); + + rtems_capture_task_stack_usage (et); +} + +/* + * rtems_capture_switch_task + * + * DESCRIPTION: + * + * This function is called when a context is switched. + * + */ +static void +rtems_capture_switch_task (rtems_tcb* current_task, + rtems_tcb* heir_task) +{ + /* + * Only perform context switch trace processing if tracing is + * enabled. + */ + if (capture_flags & RTEMS_CAPTURE_ON) + { + uint32_t ticks; + uint32_t tick_offset; + + /* + * Get the cpature task control block so we can update the + * reference and perform any watch or trigger functions. + * The task pointers may not be known as the task may have + * been created before the capture engine was open. Add them. + */ + rtems_capture_task_t* ct; + rtems_capture_task_t* ht; + + + if (_States_Is_transient (current_task->current_state) + || _States_Is_dormant (current_task->current_state)) + { + rtems_id ct_id = current_task->Object.id; + + for (ct = capture_tasks; ct; ct = ct->forw) + if (ct->id == ct_id) + break; + } + else + { + ct = current_task->extensions[capture_extension_index]; + + if (ct == NULL) + ct = rtems_capture_create_capture_task (current_task); + } + + ht = heir_task->extensions[capture_extension_index]; + + if (ht == NULL) + ht = rtems_capture_create_capture_task (heir_task); + + /* + * Update the execution time. Assume the tick will not overflow + * for now. This may need to change. + */ + rtems_capture_get_time (&ticks, &tick_offset); + + /* + * We could end up with null pointers for both the current task + * and the heir task. + */ + + if (ht) + { + ht->in++; + ht->ticks_in = ticks; + ht->tick_offset_in = tick_offset; + } + + if (ct) + { + ct->out++; + ct->ticks += ticks - ct->ticks_in; + + if (capture_timestamp) + { + tick_offset += capture_tick_period - ct->tick_offset_in; + + if (tick_offset < capture_tick_period) + ct->tick_offset = tick_offset; + else + { + ct->ticks++; + ct->tick_offset = tick_offset - capture_tick_period; + } + } + else + { + ct->tick_offset += 100; + } + } + + if (rtems_capture_trigger (ct, ht, RTEMS_CAPTURE_SWITCH)) + { + rtems_capture_record (ct, RTEMS_CAPTURE_SWITCHED_OUT_EVENT); + rtems_capture_record (ht, RTEMS_CAPTURE_SWITCHED_IN_EVENT); + } + } +} + +/* + * rtems_capture_open + * + * DESCRIPTION: + * + * This function initialises the realtime capture engine allocating the trace + * buffer. It is assumed we have a working heap at stage of initialisation. + * + */ +rtems_status_code +rtems_capture_open (uint32_t size, rtems_capture_timestamp timestamp __attribute__((unused))) +{ + rtems_extensions_table capture_extensions; + rtems_name name; + rtems_status_code sc; + + /* + * See if the capture engine is already open. + */ + + if (capture_records) + return RTEMS_RESOURCE_IN_USE; + + capture_records = malloc (size * sizeof (rtems_capture_record_t)); + + if (capture_records == NULL) + return RTEMS_NO_MEMORY; + + capture_size = size; + capture_count = 0; + capture_in = capture_records; + capture_out = 0; + capture_flags = 0; + capture_tasks = NULL; + capture_ceiling = 0; + capture_floor = 255; + + /* + * Create the extension table. This is copied so we + * can create it as a local. + */ + capture_extensions.thread_create = rtems_capture_create_task; + capture_extensions.thread_start = rtems_capture_start_task; + capture_extensions.thread_restart = rtems_capture_restart_task; + capture_extensions.thread_delete = rtems_capture_delete_task; + capture_extensions.thread_switch = rtems_capture_switch_task; + capture_extensions.thread_begin = rtems_capture_begin_task; + capture_extensions.thread_exitted = rtems_capture_exitted_task; + capture_extensions.fatal = NULL; + + /* + * Get the tick period from the BSP Configuration Table. + */ + capture_tick_period = Configuration.microseconds_per_tick; + + /* + * Register the user extension handlers for the CAPture Engine. + */ + name = rtems_build_name ('C', 'A', 'P', 'E'); + sc = rtems_extension_create (name, &capture_extensions, &capture_id); + + if (sc != RTEMS_SUCCESSFUL) + { + capture_id = 0; + free (capture_records); + capture_records = NULL; + } + else + { + capture_extension_index = rtems_object_id_get_index (capture_id);; + } + + /* + * Iterate over the list of existing tasks. + */ + + return sc; +} + +/* + * rtems_capture_close + * + * DESCRIPTION: + * + * This function shutdowns the capture engine and release any claimed + * resources. + */ +rtems_status_code +rtems_capture_close (void) +{ + rtems_interrupt_level level; + rtems_capture_task_t* task; + rtems_capture_control_t* control; + rtems_capture_record_t* records; + rtems_status_code sc; + + rtems_interrupt_disable (level); + + if (!capture_records) + { + rtems_interrupt_enable (level); + return RTEMS_SUCCESSFUL; + } + + capture_flags &= ~(RTEMS_CAPTURE_ON | RTEMS_CAPTURE_ONLY_MONITOR); + + records = capture_records; + capture_records = NULL; + + rtems_interrupt_enable (level); + + /* + * Delete the extension first. This means we are now able to + * release the resources we have without them being used. + */ + + sc = rtems_extension_delete (capture_id); + + if (sc != RTEMS_SUCCESSFUL) + return sc; + + task = capture_tasks; + + while (task) + { + rtems_capture_task_t* delete = task; + task = task->forw; + _Workspace_Free (delete); + } + + capture_tasks = NULL; + + control = capture_controls; + + while (control) + { + rtems_capture_control_t* delete = control; + control = control->next; + _Workspace_Free (delete); + } + + capture_controls = NULL; + + if (capture_records) + { + free (capture_records); + capture_records = NULL; + } + + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_control + * + * DESCRIPTION: + * + * This function allows control of tracing at a global level. + */ +rtems_status_code +rtems_capture_control (bool enable) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + if (!capture_records) + { + rtems_interrupt_enable (level); + return RTEMS_UNSATISFIED; + } + + if (enable) + capture_flags |= RTEMS_CAPTURE_ON; + else + capture_flags &= ~RTEMS_CAPTURE_ON; + + rtems_interrupt_enable (level); + + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_monitor + * + * DESCRIPTION: + * + * This function enable the monitor mode. When in the monitor mode + * the tasks are monitored but no data is saved. This can be used + * to profile the load on a system. + */ +rtems_status_code +rtems_capture_monitor (bool enable) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + if (!capture_records) + { + rtems_interrupt_enable (level); + return RTEMS_UNSATISFIED; + } + + if (enable) + capture_flags |= RTEMS_CAPTURE_ONLY_MONITOR; + else + capture_flags &= ~RTEMS_CAPTURE_ONLY_MONITOR; + + rtems_interrupt_enable (level); + + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_flush + * + * DESCRIPTION: + * + * This function flushes the capture buffer. The prime parameter allows the + * capture engine to also be primed again. + */ +rtems_status_code +rtems_capture_flush (bool prime) +{ + rtems_interrupt_level level; + rtems_capture_task_t* task; + + rtems_interrupt_disable (level); + + for (task = capture_tasks; task != NULL; task = task->forw) + { + task->flags &= ~RTEMS_CAPTURE_TRACED; + task->refcount = 0; + } + + if (prime) + capture_flags &= ~(RTEMS_CAPTURE_TRIGGERED | RTEMS_CAPTURE_OVERFLOW); + else + capture_flags &= ~RTEMS_CAPTURE_OVERFLOW; + + capture_count = 0; + capture_in = capture_records; + capture_out = 0; + + rtems_interrupt_enable (level); + + task = capture_tasks; + + while (task) + { + rtems_capture_task_t* check = task; + task = task->forw; + rtems_capture_destroy_capture_task (check); + } + + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_watch_add + * + * DESCRIPTION: + * + * This function defines a watch for a specific task given a name. A watch + * causes it to be traced either in or out of context. The watch can be + * optionally enabled or disabled with the set routine. It is disabled by + * default. + */ +rtems_status_code +rtems_capture_watch_add (rtems_name name, rtems_id id) +{ + rtems_capture_control_t* control; + + if ((name == 0) && (id == 0)) + return RTEMS_UNSATISFIED; + + control = rtems_capture_find_control (name, id); + + if (control && !id) + return RTEMS_TOO_MANY; + + if (!control) + control = rtems_capture_create_control (name, id); + + if (!control) + return RTEMS_NO_MEMORY; + + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_watch_del + * + * DESCRIPTION: + * + * This function removes a watch for a specific task given a name. The task + * description will still exist if referenced by a trace record in the trace + * buffer or a global watch is defined. + */ +rtems_status_code +rtems_capture_watch_del (rtems_name name, rtems_id id) +{ + rtems_interrupt_level level; + rtems_capture_control_t* control; + rtems_capture_control_t** prev_control; + rtems_capture_task_t* task; + bool found = false; + + /* + * Should this test be for wildcards ? + */ + + for (prev_control = &capture_controls, control = capture_controls; + control != NULL; ) + { + if (rtems_capture_match_name_id (control->name, control->id, name, id)) + { + rtems_interrupt_disable (level); + + for (task = capture_tasks; task != NULL; task = task->forw) + if (task->control == control) + task->control = 0; + + *prev_control = control->next; + + rtems_interrupt_enable (level); + + _Workspace_Free (control); + + control = *prev_control; + + found = true; + } + else + { + prev_control = &control->next; + control = control->next; + } + } + + if (found) + return RTEMS_SUCCESSFUL; + + return RTEMS_INVALID_NAME; +} + +/* + * rtems_capture_watch_set + * + * DESCRIPTION: + * + * This function allows control of a watch. The watch can be enabled or + * disabled. + */ +rtems_status_code +rtems_capture_watch_ctrl (rtems_name name, rtems_id id, bool enable) +{ + rtems_interrupt_level level; + rtems_capture_control_t* control; + bool found = false; + + /* + * Find the control and then set the watch. It must exist before it can + * be controlled. + */ + for (control = capture_controls; control != NULL; control = control->next) + { + if (rtems_capture_match_name_id (control->name, control->id, name, id)) + { + rtems_interrupt_disable (level); + + if (enable) + control->flags |= RTEMS_CAPTURE_WATCH; + else + control->flags &= ~RTEMS_CAPTURE_WATCH; + + rtems_interrupt_enable (level); + + found = true; + } + } + + if (found) + return RTEMS_SUCCESSFUL; + + return RTEMS_INVALID_NAME; +} + +/* + * rtems_capture_watch_global + * + * DESCRIPTION: + * + * This function allows control of a global watch. The watch can be enabled or + * disabled. A global watch configures all tasks below the ceiling and above + * the floor to be traced. + */ +rtems_status_code +rtems_capture_watch_global (bool enable) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + /* + * We need to keep specific and global watches separate so + * a global enable/disable does not lose a specific watch. + */ + if (enable) + capture_flags |= RTEMS_CAPTURE_GLOBAL_WATCH; + else + capture_flags &= ~RTEMS_CAPTURE_GLOBAL_WATCH; + + rtems_interrupt_enable (level); + + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_watch_global_on + * + * DESCRIPTION: + * + * This function returns the global watch state. + */ +bool +rtems_capture_watch_global_on (void) +{ + return capture_flags & RTEMS_CAPTURE_GLOBAL_WATCH ? 1 : 0; +} + +/* + * rtems_capture_watch_ceiling + * + * DESCRIPTION: + * + * This function sets a watch ceiling. Tasks at or greating that the + * ceiling priority are not watched. This is a simple way to monitor + * an application and exclude system tasks running at a higher + * priority level. + */ +rtems_status_code +rtems_capture_watch_ceiling (rtems_task_priority ceiling) +{ + capture_ceiling = ceiling; + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_watch_get_ceiling + * + * DESCRIPTION: + * + * This function gets the watch ceiling. + */ +rtems_task_priority +rtems_capture_watch_get_ceiling (void) +{ + return capture_ceiling; +} + +/* + * rtems_capture_watch_floor + * + * DESCRIPTION: + * + * This function sets a watch floor. Tasks at or less that the + * floor priority are not watched. This is a simple way to monitor + * an application and exclude system tasks running at a lower + * priority level. + */ +rtems_status_code +rtems_capture_watch_floor (rtems_task_priority floor) +{ + capture_floor = floor; + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_watch_get_floor + * + * DESCRIPTION: + * + * This function gets the watch floor. + */ +rtems_task_priority +rtems_capture_watch_get_floor (void) +{ + return capture_floor; +} + +/* + * rtems_capture_map_trigger + * + * DESCRIPTION: + * + * Map the trigger to a bit mask. + * + */ +uint32_t +rtems_capture_map_trigger (rtems_capture_trigger_t trigger) +{ + /* + * Transform the mode and trigger to a bit map. + */ + switch (trigger) + { + case rtems_capture_switch: + return RTEMS_CAPTURE_SWITCH; + case rtems_capture_create: + return RTEMS_CAPTURE_CREATE; + case rtems_capture_start: + return RTEMS_CAPTURE_START; + case rtems_capture_restart: + return RTEMS_CAPTURE_RESTART; + case rtems_capture_delete: + return RTEMS_CAPTURE_DELETE; + case rtems_capture_begin: + return RTEMS_CAPTURE_BEGIN; + case rtems_capture_exitted: + return RTEMS_CAPTURE_EXITTED; + default: + break; + } + return 0; +} + +/* + * rtems_capture_set_trigger + * + * DESCRIPTION: + * + * This function sets a trigger. + * + * This set trigger routine will create a capture control for the + * target task. The task list is searched and any existing tasks + * are linked to the new control. + * + * We can have a number of tasks that have the same name so we + * search using names. This means a number of tasks can be + * linked to single control. + */ +rtems_status_code +rtems_capture_set_trigger (rtems_name from_name, + rtems_id from_id, + rtems_name to_name, + rtems_id to_id, + rtems_capture_trigger_mode_t mode, + rtems_capture_trigger_t trigger) +{ + rtems_capture_control_t* control; + uint32_t flags; + + flags = rtems_capture_map_trigger (trigger); + + /* + * The mode sets the opposite type of trigger. For example + * FROM ANY means trigger when the event happens TO this + * task. TO ANY means FROM this task. + */ + + if (mode == rtems_capture_to_any) + { + control = rtems_capture_create_control (from_name, from_id); + if (control == NULL) + return RTEMS_NO_MEMORY; + control->from_triggers |= flags & RTEMS_CAPTURE_FROM_TRIGS; + } + else + { + control = rtems_capture_create_control (to_name, to_id); + if (control == NULL) + return RTEMS_NO_MEMORY; + if (mode == rtems_capture_from_any) + control->to_triggers |= flags; + else + { + bool done = false; + int i; + + control->by_triggers |= flags; + + for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++) + { + if (rtems_capture_control_by_valid (control, i) && + ((control->by[i].name == from_name) || + (from_id && (control->by[i].id == from_id)))) + { + control->by[i].trigger |= flags; + done = true; + break; + } + } + + if (!done) + { + for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++) + { + if (!rtems_capture_control_by_valid (control, i)) + { + control->by_valid |= RTEMS_CAPTURE_CONTROL_FROM_MASK (i); + control->by[i].name = from_name; + control->by[i].id = from_id; + control->by[i].trigger = flags; + done = true; + break; + } + } + } + + if (!done) + return RTEMS_TOO_MANY; + } + } + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_clear_trigger + * + * DESCRIPTION: + * + * This function clear a trigger. + */ +rtems_status_code +rtems_capture_clear_trigger (rtems_name from_name, + rtems_id from_id, + rtems_name to_name, + rtems_id to_id, + rtems_capture_trigger_mode_t mode, + rtems_capture_trigger_t trigger) +{ + rtems_capture_control_t* control; + uint32_t flags; + + flags = rtems_capture_map_trigger (trigger); + + if (mode == rtems_capture_to_any) + { + control = rtems_capture_find_control (from_name, from_id); + if (control == NULL) + { + if (from_id) + return RTEMS_INVALID_ID; + return RTEMS_INVALID_NAME; + } + control->from_triggers &= ~flags; + } + else + { + control = rtems_capture_find_control (to_name, to_id); + if (control == NULL) + { + if (to_id) + return RTEMS_INVALID_ID; + return RTEMS_INVALID_NAME; + } + if (mode == rtems_capture_from_any) + control->to_triggers &= ~flags; + else + { + bool done = false; + int i; + + control->by_triggers &= ~flags; + + for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++) + { + if (rtems_capture_control_by_valid (control, i) && + ((control->by[i].name == from_name) || + (control->by[i].id == from_id))) + { + control->by[i].trigger &= ~trigger; + if (control->by[i].trigger == 0) + control->by_valid &= ~RTEMS_CAPTURE_CONTROL_FROM_MASK (i); + done = true; + break; + } + } + + if (!done) + { + if (from_id) + return RTEMS_INVALID_ID; + return RTEMS_INVALID_NAME; + } + } + } + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_read + * + * DESCRIPTION: + * + * This function reads a number of records from the capture buffer. + * The user can optionally block and wait until the buffer as a + * specific number of records available or a specific time has + * elasped. + * + * The function returns the number of record that is has that are + * in a continous block of memory. If the number of available records + * wrap then only those records are provided. This removes the need for + * caller to be concerned about buffer wrappings. If the number of + * requested records cannot be met due to the wrapping of the records + * less than the specified number will be returned. + * + * The user must release the records. This is achieved with a call to + * rtems_capture_release. Calls this function without a release will + * result in at least the same number of records being released. + * + * The 'threshold' parameter is the number of records that must be + * captured before returning. If a timeout period is specified (non-0) + * any captured records will be returned. These parameters stop + * thrashing occuring for a small number of records, yet allows + * a user configured latiency to be applied for single events. + * + * The 'timeout' parameter is in micro-seconds. A value of 0 will disable + * the timeout. + * + */ +rtems_status_code +rtems_capture_read (uint32_t threshold, + uint32_t timeout, + uint32_t* read, + rtems_capture_record_t** recs) +{ + rtems_interrupt_level level; + rtems_status_code sc = RTEMS_SUCCESSFUL; + uint32_t count; + + *read = 0; + *recs = NULL; + + rtems_interrupt_disable (level); + + /* + * Only one reader is allowed. + */ + + if (capture_flags & RTEMS_CAPTURE_READER_ACTIVE) + { + rtems_interrupt_enable (level); + return RTEMS_RESOURCE_IN_USE; + } + + capture_flags |= RTEMS_CAPTURE_READER_ACTIVE; + *read = count = capture_count; + + rtems_interrupt_enable (level); + + *recs = &capture_records[capture_out]; + + for (;;) + { + /* + * See if the count wraps the end of the record buffer. + */ + if (count && ((capture_out + count) >= capture_size)) + *read = capture_size - capture_out; + + /* + * Do we have a threshold and the current count has not wrapped + * around the end of the capture record buffer ? + */ + if ((*read == count) && threshold) + { + /* + * Do we have enough records ? + */ + if (*read < threshold) + { + rtems_event_set event_out; + + rtems_task_ident (RTEMS_SELF, RTEMS_LOCAL, &capture_reader); + + rtems_interrupt_disable (level); + + capture_flags |= RTEMS_CAPTURE_READER_WAITING; + + rtems_interrupt_enable (level); + + sc = rtems_event_receive (RTEMS_EVENT_0, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_MICROSECONDS_TO_TICKS (timeout), + &event_out); + + /* + * Let the user handle all other sorts of errors. This may + * not be the best solution, but oh well, it will do for + * now. + */ + if ((sc != RTEMS_SUCCESSFUL) && (sc != RTEMS_TIMEOUT)) + break; + + rtems_interrupt_disable (level); + + *read = count = capture_count; + + rtems_interrupt_enable (level); + + continue; + } + } + + /* + * Always out if we reach here. To loop use continue. + */ + break; + } + + return sc; +} + +/* + * rtems_capture_release + * + * DESCRIPTION: + * + * This function releases the requested number of record slots back + * to the capture engine. The count must match the number read. + */ +rtems_status_code +rtems_capture_release (uint32_t count) +{ + rtems_capture_record_t* rec; + uint32_t counted; + + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + if (count > capture_count) + count = capture_count; + + rtems_interrupt_enable (level); + + counted = count; + + rec = &capture_records[capture_out]; + + while (counted--) + { + rtems_capture_refcount_down (rec->task); + rtems_capture_destroy_capture_task (rec->task); + rec++; + } + + rtems_interrupt_disable (level); + + capture_count -= count; + + capture_out = (capture_out + count) % capture_size; + + capture_flags &= ~RTEMS_CAPTURE_READER_ACTIVE; + + rtems_interrupt_enable (level); + + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_tick_time + * + * DESCRIPTION: + * + * This function returns the tick period in nano-seconds. + */ +uint32_t +rtems_capture_tick_time (void) +{ + return capture_tick_period; +} + +/* + * rtems_capture_event_text + * + * DESCRIPTION: + * + * This function returns a string for an event based on the bit in the + * event. The functions takes the bit offset as a number not the bit + * set in a bit map. + */ +const char* +rtems_capture_event_text (int event) +{ + if ((event < RTEMS_CAPTURE_EVENT_START) || (event > RTEMS_CAPTURE_EVENT_END)) + return "invalid event id"; + return capture_event_text[event - RTEMS_CAPTURE_EVENT_START]; +} + +/* + * rtems_capture_get_task_list + * + * DESCRIPTION: + * + * This function returns the head of the list of tasks that the + * capture engine has detected. + */ +rtems_capture_task_t* +rtems_capture_get_task_list (void) +{ + return capture_tasks; +} + +/* + * rtems_capture_task_stack_usage + * + * DESCRIPTION: + * + * This function updates the stack usage. The task control block + * is updated. + */ +uint32_t +rtems_capture_task_stack_usage (rtems_capture_task_t* task) +{ + if (task->tcb) + { + uint32_t* st; + uint32_t* s; + + /* + * @todo: Assumes all stacks move the same way. + */ + st = task->tcb->Start.Initial_stack.area + task->stack_size; + s = task->tcb->Start.Initial_stack.area; + + while (s < st) + { + if (*s != 0xdeaddead) + break; + s++; + } + + task->stack_clean = + s - (uint32_t*) task->tcb->Start.Initial_stack.area; + } + + return task->stack_clean; +} + +/* + * rtems_capture_get_control_list + * + * DESCRIPTION: + * + * This function returns the head of the list of control in the + * capture engine. + */ +rtems_capture_control_t* +rtems_capture_get_control_list (void) +{ + return capture_controls; +} diff --git a/cpukit/libmisc/capture/capture.h b/cpukit/libmisc/capture/capture.h new file mode 100644 index 0000000000..48ba5ebec4 --- /dev/null +++ b/cpukit/libmisc/capture/capture.h @@ -0,0 +1,1042 @@ +/** + * @file rtems/capture.h + */ + +/* + ------------------------------------------------------------------------ + $Id$ + ------------------------------------------------------------------------ + + Copyright Objective Design Systems Pty Ltd, 2002 + All rights reserved Objective Design Systems Pty Ltd, 2002 + Chris Johns (ccj@acm.org) + + COPYRIGHT (c) 1989-1998. + On-Line Applications Research Corporation (OAR). + + The license and distribution terms for this file may be + found in the file LICENSE in this distribution. + + This software with is provided ``as is'' and with NO WARRANTY. + + ------------------------------------------------------------------------ + + RTEMS Performance Monitoring and Measurement Framework. + This is the Capture Engine component. + +*/ + +#ifndef __CAPTURE_H_ +#define __CAPTURE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rtems.h> + +/** + * The number of tasks in a trigger group. + */ +#define RTEMS_CAPTURE_TRIGGER_TASKS (32) + +/** + * rtems_capture_from_t + * + * DESCRIPTION: + * + * A from capture is a task id and a mask for the type of + * from trigger we are interested in. The mask uses the same + * bit maps as the flags field in the control structure. There + * will only be a from type trigger if the flags in the control + * structure has the specific *_BY bit set. + */ +typedef struct rtems_capture_from_s +{ + rtems_name name; + rtems_id id; + uint32_t trigger; +} rtems_capture_from_t; + +/** + * rtems_capture_control_t + * + * DESCRIPTION: + * + * RTEMS control holds the trigger and watch configuration for a group of + * tasks with the same name. The flags hold global control flags. + * + * The to_triggers fields holds triggers TO this task. The from_triggers holds + * triggers from this task. The by_triggers is an OR or triggers which are + * caused BY the task listed TO this task. The by_valid flag which entries + * in by are valid. + */ +typedef struct rtems_capture_control_s +{ + rtems_name name; + rtems_id id; + uint32_t flags; + uint32_t to_triggers; + uint32_t from_triggers; + uint32_t by_triggers; + uint32_t by_valid; + rtems_capture_from_t by[RTEMS_CAPTURE_TRIGGER_TASKS]; + struct rtems_capture_control_s* next; +} rtems_capture_control_t; + +/** + * The from_valid mask. + */ +#define RTEMS_CAPTURE_CONTROL_FROM_MASK(_s) \ + (UINT32_C(1) << (RTEMS_CAPTURE_TRIGGER_TASKS - ((_s) + 1))) + +/** + * Control flags. + */ +#define RTEMS_CAPTURE_WATCH (1 << 0) + +/** + * Control triggers. + */ +#define RTEMS_CAPTURE_SWITCH (1 << 0) +#define RTEMS_CAPTURE_CREATE (1 << 1) +#define RTEMS_CAPTURE_START (1 << 2) +#define RTEMS_CAPTURE_RESTART (1 << 3) +#define RTEMS_CAPTURE_DELETE (1 << 4) +#define RTEMS_CAPTURE_BEGIN (1 << 5) +#define RTEMS_CAPTURE_EXITTED (1 << 6) + +#define RTEMS_CAPTURE_FROM_TRIGS (RTEMS_CAPTURE_SWITCH | \ + RTEMS_CAPTURE_CREATE | \ + RTEMS_CAPTURE_START | \ + RTEMS_CAPTURE_RESTART | \ + RTEMS_CAPTURE_DELETE) + +#define RTEMS_CAPTURE_TO_TRIGS (RTEMS_CAPTURE_SWITCH | \ + RTEMS_CAPTURE_CREATE | \ + RTEMS_CAPTURE_START | \ + RTEMS_CAPTURE_RESTART | \ + RTEMS_CAPTURE_DELETE | \ + RTEMS_CAPTURE_BEGIN | \ + RTEMS_CAPTURE_EXITTED) + +/** + * rtems_capture_task_t + * + * DESCRIPTION: + * + * RTEMS capture control provdes the information about a task, along + * with its trigger state. The control is referenced by each + * capture record. This is information neeed by the decoder. The + * capture record cannot assume the task will exist when the record is + * dumped via the target interface so task info needed for tracing is + * copied and held here. Once the references in the trace buffer + * have been removed and the task is deleted this structure is + * released back to the heap. + * + * The inline helper functions provide more details about the info + * contained in this structure. + * + * Note, the tracer code exploits the fact an rtems_name is a + * 32bit value. + */ +typedef struct rtems_capture_task_s +{ + rtems_name name; + rtems_id id; + uint32_t flags; + uint32_t refcount; + rtems_tcb* tcb; + uint32_t in; + uint32_t out; + rtems_task_priority start_priority; + uint32_t stack_size; + uint32_t stack_clean; + uint32_t ticks; + uint32_t tick_offset; + uint32_t ticks_in; + uint32_t tick_offset_in; + uint32_t last_ticks; + uint32_t last_tick_offset; + rtems_capture_control_t* control; + struct rtems_capture_task_s* forw; + struct rtems_capture_task_s* back; +} rtems_capture_task_t; + +/** + * Task flags. + */ +#define RTEMS_CAPTURE_TRACED (1 << 0) + +/* + * rtems_capture_record_t + * + * DESCRIPTION: + * + * RTEMS capture record. This is a record that is written into + * the buffer. The events includes the priority of the task + * at the time of the context switch. + */ +typedef struct rtems_capture_record_s +{ + rtems_capture_task_t* task; + uint32_t events; + uint32_t ticks; + uint32_t tick_offset; +} rtems_capture_record_t; + +/** + * The capture record event flags. + */ +#define RTEMS_CAPTURE_REAL_PRI_EVENT_MASK UINT32_C (0x000000ff) +#define RTEMS_CAPTURE_CURR_PRI_EVENT_MASK UINT32_C (0x0000ff00) +#define RTEMS_CAPTURE_REAL_PRIORITY_EVENT (0) +#define RTEMS_CAPTURE_CURR_PRIORITY_EVENT (8) +#define RTEMS_CAPTURE_EVENT_START (16) +#define RTEMS_CAPTURE_CREATED_BY_EVENT UINT32_C (0x00010000) +#define RTEMS_CAPTURE_CREATED_EVENT UINT32_C (0x00020000) +#define RTEMS_CAPTURE_STARTED_BY_EVENT UINT32_C (0x00040000) +#define RTEMS_CAPTURE_STARTED_EVENT UINT32_C (0x00080000) +#define RTEMS_CAPTURE_RESTARTED_BY_EVENT UINT32_C (0x00100000) +#define RTEMS_CAPTURE_RESTARTED_EVENT UINT32_C (0x00200000) +#define RTEMS_CAPTURE_DELETED_BY_EVENT UINT32_C (0x00400000) +#define RTEMS_CAPTURE_DELETED_EVENT UINT32_C (0x00800000) +#define RTEMS_CAPTURE_BEGIN_EVENT UINT32_C (0x01000000) +#define RTEMS_CAPTURE_EXITTED_EVENT UINT32_C (0x02000000) +#define RTEMS_CAPTURE_SWITCHED_OUT_EVENT UINT32_C (0x04000000) +#define RTEMS_CAPTURE_SWITCHED_IN_EVENT UINT32_C (0x08000000) +#define RTEMS_CAPTURE_TIMESTAMP UINT32_C (0x10000000) +#define RTEMS_CAPTURE_EVENT_END (28) + +/** + * rtems_capture_trigger_mode_t + * + * DESCRIPTION: + * + * The types of trigger modes that exist. + */ +typedef enum rtems_capture_trigger_mode_e +{ + rtems_capture_to_any, + rtems_capture_from_any, + rtems_capture_from_to +} rtems_capture_trigger_mode_t; + +/** + * rtems_capture_trigger_t + * + * DESCRIPTION: + * + * The types of triggers that exist. + */ +typedef enum rtems_capture_trigger_e +{ + rtems_capture_switch, + rtems_capture_create, + rtems_capture_start, + rtems_capture_restart, + rtems_capture_delete, + rtems_capture_begin, + rtems_capture_exitted +} rtems_capture_trigger_t; + +/** + * rtems_capture_timestamp + * + * DESCRIPTION: + * + * This defines the callout handler to obtain a time stamp. The + * value returned is time count since the last read. + * + */ + +typedef void (*rtems_capture_timestamp) + (uint32_t* ticks, uint32_t* micro); + +/** + * rtems_capture_open + * + * DESCRIPTION: + * + * This function initialises the realtime trace manager allocating the + * capture buffer. It is assumed we have a working heap at stage of + * initialisation. + * + */ +rtems_status_code +rtems_capture_open (uint32_t size, + rtems_capture_timestamp timestamp); + +/** + * rtems_capture_close + * + * DESCRIPTION: + * + * This function shutdowns the tracer and release any claimed + * resources. + */ +rtems_status_code +rtems_capture_close (void); + +/** + * rtems_capture_control + * + * DESCRIPTION: + * + * This function allows control of tracing at a global level. + */ +rtems_status_code +rtems_capture_control (bool enable); + +/** + * rtems_capture_monitor + * + * DESCRIPTION: + * + * This function enable the monitor mode. When in the monitor mode + * the tasks are monitored but no data is saved. This can be used + * to profile the load on a system. + */ +rtems_status_code +rtems_capture_monitor (bool enable); + +/* + * rtems_capture_flush + * + * DESCRIPTION: + * + * This function flushes the trace buffer. The prime parameter allows the + * capture engine to also be primed again. + */ +rtems_status_code +rtems_capture_flush (bool prime); + +/** + * rtems_capture_watch_add + * + * DESCRIPTION: + * + * This function defines a watch for a specific task given a name. A watch + * causes it to be traced either in or out of context. The watch can be + * optionally enabled or disabled with the set routine. It is disabled by + * default. + */ +rtems_status_code +rtems_capture_watch_add (rtems_name name, rtems_id id); + +/** + * rtems_capture_watch_del + * + * DESCRIPTION: + * + * This function removes a watch for a specific task given a name. The task + * description will still exist if referenced by a trace record in the trace + * buffer or a global watch is defined. + */ +rtems_status_code +rtems_capture_watch_del (rtems_name name, rtems_id id); + +/** + * rtems_capture_watch_set + * + * DESCRIPTION: + * + * This function allows control of a watch. The watch can be enabled or + * disabled. + */ +rtems_status_code +rtems_capture_watch_ctrl (rtems_name name, + rtems_id id, + bool enable); + +/** + * rtems_capture_watch_global + * + * DESCRIPTION: + * + * This function allows control of a global watch. The watch can + * be enabled or disabled. A global watch configures all tasks below + * the ceiling and above the floor to be traced. + */ +rtems_status_code +rtems_capture_watch_global (bool enable); + +/** + * rtems_capture_watch_global_on + * + * DESCRIPTION: + * + * This function returns the global watch state. + */ +bool +rtems_capture_watch_global_on (void); + +/** + * rtems_capture_watch_ceiling + * + * DESCRIPTION: + * + * This function sets a watch ceiling. Tasks at or greating that the + * ceiling priority are not watched. This is a simple way to monitor + * an application and exclude system tasks running at a higher + * priority level. + */ +rtems_status_code +rtems_capture_watch_ceiling (rtems_task_priority ceiling); + +/** + * rtems_capture_watch_get_ceiling + * + * DESCRIPTION: + * + * This function gets the watch ceiling. + */ +rtems_task_priority +rtems_capture_watch_get_ceiling (void); + +/** + * rtems_capture_watch_floor + * + * DESCRIPTION: + * + * This function sets a watch floor. Tasks at or less that the + * floor priority are not watched. This is a simple way to monitor + * an application and exclude system tasks running at a lower + * priority level. + */ +rtems_status_code +rtems_capture_watch_floor (rtems_task_priority floor); + +/** + * rtems_capture_watch_get_floor + * + * DESCRIPTION: + * + * This function gets the watch floor. + */ +rtems_task_priority +rtems_capture_watch_get_floor (void); + +/** + * rtems_capture_set_trigger + * + * DESCRIPTION: + * + * This function sets a trigger. + * + * This set trigger routine will create a trace control for the + * target task. The task list is searched and any existing tasks + * are linked to the new control. + * + * We can have a number of tasks that have the same name so we + * search using names. This means a number of tasks can be + * linked to single control. + */ +rtems_status_code +rtems_capture_set_trigger (rtems_name from_name, + rtems_id from_id, + rtems_name to_name, + rtems_id to_id, + rtems_capture_trigger_mode_t mode, + rtems_capture_trigger_t trigger); + +/** + * rtems_capture_clear_trigger + * + * DESCRIPTION: + * + * This function clears a trigger. + * + * This clear trigger routine will not clear a watch. + */ +rtems_status_code +rtems_capture_clear_trigger (rtems_name from_name, + rtems_id from_id, + rtems_name to_name, + rtems_id to_id, + rtems_capture_trigger_mode_t mode, + rtems_capture_trigger_t trigger); + +/** + * rtems_capture_read + * + * DESCRIPTION: + * + * This function reads a number of records from the capture buffer. + * The user can optionally block and wait until the buffer as a + * specific number of records available or a specific time has + * elasped. + * + * The function returns the number of record that is has that are + * in a continous block of memory. If the number of available records + * wrap then only those records are provided. This removes the need for + * caller to be concerned about buffer wrappings. If the number of + * requested records cannot be met due to the wrapping of the records + * less than the specified number will be returned. + * + * The user must release the records. This is achieved with a call to + * rtems_capture_release. Calls this function without a release will + * result in at least the same number of records being released. + * + * The 'threshold' parameter is the number of records that must be + * captured before returning. If a timeout period is specified (non-0) + * any captured records will be returned. These parameters stop + * thrashing occuring for a small number of records, yet allows + * a user configured latiency to be applied for single events. + * + * The 'timeout' parameter is in micro-seconds. A value of 0 will + * disable the timeout. + * + */ +rtems_status_code +rtems_capture_read (uint32_t threshold, + uint32_t timeout, + uint32_t* read, + rtems_capture_record_t** recs); + +/** + * rtems_capture_release + * + * DESCRIPTION: + * + * This function releases the requested number of record slots back + * to the capture engine. The count must match the number read. + */ +rtems_status_code +rtems_capture_release (uint32_t count); + +/** + * rtems_capture_tick_time + * + * DESCRIPTION: + * + * This function returns the tick period in micro-seconds. + */ +uint32_t +rtems_capture_tick_time (void); + +/* + * rtems_capture_tick_time + * + * DESCRIPTION: + * + * This function returns the tick period in micro-seconds. + */ +uint32_t +rtems_capture_tick_time (void); + +/** + * rtems_capture_event_text + * + * DESCRIPTION: + * + * This function returns a string for an event based on the bit in the + * event. The functions takes the bit offset as a number not the bit + * set in a bit map. + */ +const char* +rtems_capture_event_text (int event); + +/** + * rtems_capture_get_task_list + * + * DESCRIPTION: + * + * This function returns the head of the list of tasks that the + * capture engine has detected. + */ +rtems_capture_task_t* +rtems_capture_get_task_list (void); + +/** + * rtems_capture_next_task + * + * DESCRIPTION: + * + * This function returns the pointer to the next task in the list. The + * pointer NULL terminates the list. + */ +static inline rtems_capture_task_t* +rtems_capture_next_task (rtems_capture_task_t* task) +{ + return task->forw; +} + +/** + * rtems_capture_task_valid + * + * DESCRIPTION: + * + * This function returns true if the task control block points to + * a valid task. + */ +static inline bool +rtems_capture_task_valid (rtems_capture_task_t* task) +{ + return task->tcb != NULL; +} + +/** + * rtems_capture_task_id + * + * DESCRIPTION: + * + * This function returns the task id. + */ +static inline rtems_id +rtems_capture_task_id (rtems_capture_task_t* task) +{ + return task->id; +} + +/** + * rtems_capture_task_state + * + * DESCRIPTION: + * + * This function returns the task state. + */ +static inline States_Control +rtems_capture_task_state (rtems_capture_task_t* task) +{ + if (rtems_capture_task_valid (task)) + return task->tcb->current_state; + return 0; +} + +/** + * rtems_capture_task_name + * + * DESCRIPTION: + * + * This function returns the task name. + */ +static inline rtems_name +rtems_capture_task_name (rtems_capture_task_t* task) +{ + return task->name; +} + +/** + * rtems_capture_task_flags + * + * DESCRIPTION: + * + * This function returns the task flags. + */ +static inline uint32_t +rtems_capture_task_flags (rtems_capture_task_t* task) +{ + return task->flags; +} + +/** + * rtems_capture_task_control + * + * DESCRIPTION: + * + * This function returns the task control if present. + */ +static inline rtems_capture_control_t* +rtems_capture_task_control (rtems_capture_task_t* task) +{ + return task->control; +} + +/** + * rtems_capture_task_control_flags + * + * DESCRIPTION: + * + * This function returns the task control flags if a control is present. + */ +static inline uint32_t +rtems_capture_task_control_flags (rtems_capture_task_t* task) +{ + if (!task->control) + return 0; + return task->control->flags; +} + +/** + * rtems_capture_task_switched_in + * + * DESCRIPTION: + * + * This function returns the number of times the task has + * been switched into context. + */ +static inline uint32_t +rtems_capture_task_switched_in (rtems_capture_task_t* task) +{ + return task->in; +} + +/** + * rtems_capture_task_switched_out + * + * DESCRIPTION: + * + * This function returns the number of times the task has + * been switched out of context. + */ +static inline uint32_t +rtems_capture_task_switched_out (rtems_capture_task_t* task) +{ + return task->out; +} + +/** + * rtems_capture_task_curr_priority + * + * DESCRIPTION: + * + * This function returns the tasks start priority. The tracer needs this + * to track where the task's priority goes. + */ +static inline rtems_task_priority +rtems_capture_task_start_priority (rtems_capture_task_t* task) +{ + return task->start_priority; +} + +/** + * rtems_capture_task_real_priority + * + * DESCRIPTION: + * + * This function returns the tasks real priority. + */ +static inline rtems_task_priority +rtems_capture_task_real_priority (rtems_capture_task_t* task) +{ + if (rtems_capture_task_valid (task)) + return task->tcb->real_priority; + return 0; +} + +/** + * rtems_capture_task_curr_priority + * + * DESCRIPTION: + * + * This function returns the tasks current priority. + */ +static inline rtems_task_priority +rtems_capture_task_curr_priority (rtems_capture_task_t* task) +{ + if (rtems_capture_task_valid (task)) + return task->tcb->current_priority; + return 0; +} + +/** + * rtems_capture_task_stack_usage + * + * DESCRIPTION: + * + * This function updates the stack usage. The task control block + * is updated. + */ +uint32_t +rtems_capture_task_stack_usage (rtems_capture_task_t* task); + +/** + * rtems_capture_task_stack_size + * + * DESCRIPTION: + * + * This function returns the task's stack size. + */ +static inline uint32_t +rtems_capture_task_stack_size (rtems_capture_task_t* task) +{ + return task->stack_size; +} + +/** + * rtems_capture_task_stack_used + * + * DESCRIPTION: + * + * This function returns the amount of stack used. + */ +static inline uint32_t +rtems_capture_task_stack_used (rtems_capture_task_t* task) +{ + return task->stack_size - task->stack_clean; +} + +/** + * rtems_capture_task_ticks + * + * DESCRIPTION: + * + * This function returns the current execution time as ticks. + */ +static inline uint32_t +rtems_capture_task_ticks (rtems_capture_task_t* task) +{ + return task->ticks; +} + +/** + * rtems_capture_task_tick_offset + * + * DESCRIPTION: + * + * This function returns the current execution time tick offset. + */ +static inline uint32_t +rtems_capture_task_tick_offset (rtems_capture_task_t* task) +{ + return task->tick_offset; +} + +/** + * rtems_capture_task_time + * + * DESCRIPTION: + * + * This function returns the current execution time. + */ +static inline unsigned long long +rtems_capture_task_time (rtems_capture_task_t* task) +{ + unsigned long long t = task->ticks; + return (t * rtems_capture_tick_time ()) + task->tick_offset;; +} + +/** + * rtems_capture_task_delta_time + * + * DESCRIPTION: + * + * This function returns the execution time as a different between the + * last time the detla time was and now. + */ +static inline unsigned long long +rtems_capture_task_delta_time (rtems_capture_task_t* task) +{ + unsigned long long t = task->ticks - task->last_ticks; + uint32_t o = task->tick_offset - task->last_tick_offset; + + task->last_ticks = task->ticks; + task->last_tick_offset = task->tick_offset; + + return (t * rtems_capture_tick_time ()) + o; +} + +/** + * rtems_capture_task_count + * + * DESCRIPTION: + * + * This function returns the number of tasks the capture + * engine knows about. + */ +static inline uint32_t +rtems_capture_task_count (void) +{ + rtems_capture_task_t* task = rtems_capture_get_task_list (); + uint32_t count = 0; + + while (task) + { + count++; + task = rtems_capture_next_task (task); + } + + return count; +} + +/** + * rtems_capture_get_control_list + * + * DESCRIPTION: + * + * This function returns the head of the list of controls in the + * capture engine. + */ +rtems_capture_control_t* +rtems_capture_get_control_list (void); + +/** + * rtems_capture_next_control + * + * DESCRIPTION: + * + * This function returns the pointer to the next control in the list. The + * pointer NULL terminates the list. + */ +static inline rtems_capture_control_t* +rtems_capture_next_control (rtems_capture_control_t* control) +{ + return control->next; +} + +/** + * rtems_capture_control_id + * + * DESCRIPTION: + * + * This function returns the control id. + */ +static inline rtems_id +rtems_capture_control_id (rtems_capture_control_t* control) +{ + return control->id; +} + +/** + * rtems_capture_control_name + * + * DESCRIPTION: + * + * This function returns the control name. + */ +static inline rtems_name +rtems_capture_control_name (rtems_capture_control_t* control) +{ + return control->name; +} + +/** + * rtems_capture_control_flags + * + * DESCRIPTION: + * + * This function returns the control flags. + */ +static inline uint32_t +rtems_capture_control_flags (rtems_capture_control_t* control) +{ + return control->flags; +} + +/** + * rtems_capture_control_to_triggers + * + * DESCRIPTION: + * + * This function returns the task control to triggers. + */ +static inline uint32_t +rtems_capture_control_to_triggers (rtems_capture_control_t* control) +{ + return control->to_triggers; +} + +/** + * rtems_capture_control_from_triggers + * + * DESCRIPTION: + * + * This function returns the task control from triggers. + */ +static inline uint32_t +rtems_capture_control_from_triggers (rtems_capture_control_t* control) +{ + return control->from_triggers; +} + +/** + * rtems_capture_control_all_by_triggers + * + * DESCRIPTION: + * + * This function returns the task control by triggers. + */ +static inline uint32_t +rtems_capture_control_all_by_triggers (rtems_capture_control_t* control) +{ + return control->by_triggers; +} + +/** + * rtems_capture_control_by_valid + * + * DESCRIPTION: + * + * This function returns the control valid BY flags. + */ +static inline int +rtems_capture_control_by_valid (rtems_capture_control_t* control, int slot) +{ + return control->by_valid & RTEMS_CAPTURE_CONTROL_FROM_MASK (slot); +} + +/** + * rtems_capture_control_by_name + * + * DESCRIPTION: + * + * This function returns the control BY task name. + */ +static inline rtems_name +rtems_capture_control_by_name (rtems_capture_control_t* control, int by) +{ + if (by < RTEMS_CAPTURE_TRIGGER_TASKS) + return control->by[by].name; + return control->by[0].name; +} + +/** + * rtems_capture_control_by_id + * + * DESCRIPTION: + * + * This function returns the control BY task id. + */ +static inline rtems_id +rtems_capture_control_by_id (rtems_capture_control_t* control, int by) +{ + if (by < RTEMS_CAPTURE_TRIGGER_TASKS) + return control->by[by].id; + return control->by[0].id; +} + +/** + * rtems_capture_control_by_triggers + * + * DESCRIPTION: + * + * This function returns the control BY task triggers. + */ +static inline uint32_t +rtems_capture_control_by_triggers (rtems_capture_control_t* control, + int by) +{ + if (by < RTEMS_CAPTURE_TRIGGER_TASKS) + return control->by[by].trigger; + return control->by[0].trigger; +} + +/** + * rtems_capture_control_count + * + * DESCRIPTION: + * + * This function returns the number of controls the capture + * engine has. + */ +static inline uint32_t +rtems_capture_control_count (void) +{ + rtems_capture_control_t* control = rtems_capture_get_control_list (); + uint32_t count = 0; + + while (control) + { + count++; + control = rtems_capture_next_control (control); + } + + return count; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cpukit/libmisc/cpuuse/README b/cpukit/libmisc/cpuuse/README new file mode 100644 index 0000000000..ac674bb0b6 --- /dev/null +++ b/cpukit/libmisc/cpuuse/README @@ -0,0 +1,21 @@ +# +# $Id$ +# + +This directory contains code to report and reset per-task CPU usage. +If the BSP supports nanosecond timestamp granularity, this this information +is very accurate. Otherwise, it is dependendent on the tick granularity. + +It provides two primary features: + + + Generate a CPU Usage Report + + Reset CPU Usage Information + +NOTES: + +1. If configured for tick granularity, CPU usage is "docked" by a + clock tick at each context switch. +2. If configured for nanosecond granularity, no work is done at each + clock tick. All bookkeeping is done as part of a context switch. + + diff --git a/cpukit/libmisc/cpuuse/cpuusagedata.c b/cpukit/libmisc/cpuuse/cpuusagedata.c new file mode 100644 index 0000000000..2d74b7eadf --- /dev/null +++ b/cpukit/libmisc/cpuuse/cpuusagedata.c @@ -0,0 +1,28 @@ +/* + * CPU Usage Reporter - Shared Data + * + * COPYRIGHT (c) 1989-2009 + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/cpuuse.h> +#ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__ + #include <rtems/score/timespec.h> +#endif + +#ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__ + struct timespec CPU_usage_Uptime_at_last_reset; +#else + uint32_t CPU_usage_Ticks_at_last_reset; +#endif diff --git a/cpukit/libmisc/cpuuse/cpuusagereport.c b/cpukit/libmisc/cpuuse/cpuusagereport.c new file mode 100644 index 0000000000..92445520b7 --- /dev/null +++ b/cpukit/libmisc/cpuuse/cpuusagereport.c @@ -0,0 +1,205 @@ +/* + * CPU Usage Reporter + * + * COPYRIGHT (c) 1989-2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <inttypes.h> + +#include <rtems/cpuuse.h> +#include <rtems/bspIo.h> + +#ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__ + #include <rtems/score/timestamp.h> +#endif + +#ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__ + extern Timestamp_Control CPU_usage_Uptime_at_last_reset; +#else + extern uint32_t CPU_usage_Ticks_at_last_reset; +#endif + +/*PAGE + * + * rtems_cpu_usage_report + */ + +void rtems_cpu_usage_report_with_plugin( + void *context, + rtems_printk_plugin_t print +) +{ + uint32_t i; + uint32_t api_index; + Thread_Control *the_thread; + Objects_Information *information; + char name[13]; + uint32_t ival, fval; + #ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__ + Timestamp_Control uptime, total, ran, uptime_at_last_reset; + #else + uint32_t total_units = 0; + #endif + + if ( !print ) + return; + + /* + * When not using nanosecond CPU usage resolution, we have to count + * the number of "ticks" we gave credit for to give the user a rough + * guideline as to what each number means proportionally. + */ + #ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__ + _Timestamp_Set_to_zero( &total ); + uptime_at_last_reset = CPU_usage_Uptime_at_last_reset; + #else + for ( api_index = 1 ; api_index <= OBJECTS_APIS_LAST ; api_index++ ) { + #if !defined(RTEMS_POSIX_API) || defined(RTEMS_DEBUG) + if ( !_Objects_Information_table[ api_index ] ) + continue; + #endif + + information = _Objects_Information_table[ api_index ][ 1 ]; + if ( information ) { + for ( i=1 ; i <= information->maximum ; i++ ) { + the_thread = (Thread_Control *)information->local_table[ i ]; + + if ( the_thread ) + total_units += the_thread->cpu_time_used; + } + } + } + #endif + + (*print)( + context, + "-------------------------------------------------------------------------------\n" + " CPU USAGE BY THREAD\n" + "------------+----------------------------------------+---------------+---------\n" + #ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__ + " ID | NAME | SECONDS | PERCENT\n" + #else + " ID | NAME | TICKS | PERCENT\n" + #endif + "------------+----------------------------------------+---------------+---------\n" + ); + + for ( api_index = 1 ; api_index <= OBJECTS_APIS_LAST ; api_index++ ) { + #if !defined(RTEMS_POSIX_API) || defined(RTEMS_DEBUG) + if ( !_Objects_Information_table[ api_index ] ) + continue; + #endif + + information = _Objects_Information_table[ api_index ][ 1 ]; + if ( information ) { + for ( i=1 ; i <= information->maximum ; i++ ) { + the_thread = (Thread_Control *)information->local_table[ i ]; + + if ( !the_thread ) + continue; + + rtems_object_get_name( the_thread->Object.id, sizeof(name), name ); + + (*print)( + context, + " 0x%08" PRIx32 " | %-38s |", + the_thread->Object.id, + name + ); + + #ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__ + /* + * If this is the currently executing thread, account for time + * since the last context switch. + */ + ran = the_thread->cpu_time_used; + if ( _Thread_Executing->Object.id == the_thread->Object.id ) { + Timestamp_Control used; + Timestamp_Control last = _Thread_Time_of_last_context_switch; + _TOD_Get_uptime( &uptime ); + _Timestamp_Subtract( &last, &uptime, &used ); + _Timestamp_Add_to( &ran, &used ); + } else { + _TOD_Get_uptime( &uptime ); + } + _Timestamp_Subtract( &uptime_at_last_reset, &uptime, &total ); + _Timestamp_Divide( &ran, &total, &ival, &fval ); + + /* + * Print the information + */ + + (*print)( context, + "%7" PRIu32 ".%06" PRIu32 " |%4" PRIu32 ".%03" PRIu32 "\n", + _Timestamp_Get_seconds( &ran ), + _Timestamp_Get_nanoseconds( &ran ) / + TOD_NANOSECONDS_PER_MICROSECOND, + ival, fval + ); + #else + if (total_units) { + uint64_t ival_64; + + ival_64 = the_thread->cpu_time_used; + ival_64 *= 100000; + ival = ival_64 / total_units; + } else { + ival = 0; + } + + fval = ival % 1000; + ival /= 1000; + (*print)( context, + "%14" PRIu32 " |%4" PRIu32 ".%03" PRIu32 "\n", + the_thread->cpu_time_used, + ival, + fval + ); + #endif + } + } + } + + #ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__ + (*print)( + context, + "------------+----------------------------------------+---------------+---------\n" + " TIME SINCE LAST CPU USAGE RESET IN SECONDS: %7" PRIu32 ".%06" PRIu32 "\n" + "-------------------------------------------------------------------------------\n", + _Timestamp_Get_seconds( &total ), + _Timestamp_Get_nanoseconds( &total ) / TOD_NANOSECONDS_PER_MICROSECOND + ); + #else + (*print)( + context, + "------------+----------------------------------------+---------------+---------\n" + " TICKS SINCE LAST SYSTEM RESET: %14" PRIu32 "\n" + " TOTAL UNITS: %14" PRIu32 "\n" + "-------------------------------------------------------------------------------\n", + _Watchdog_Ticks_since_boot - CPU_usage_Ticks_at_last_reset, + total_units + ); + #endif +} + +void rtems_cpu_usage_report( void ) +{ + rtems_cpu_usage_report_with_plugin( NULL, printk_plugin ); +} diff --git a/cpukit/libmisc/cpuuse/cpuusagereset.c b/cpukit/libmisc/cpuuse/cpuusagereset.c new file mode 100644 index 0000000000..0bf46903f1 --- /dev/null +++ b/cpukit/libmisc/cpuuse/cpuusagereset.c @@ -0,0 +1,60 @@ +/* + * CPU Usage Reporter + * + * COPYRIGHT (c) 1989-2009 + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/score/timestamp.h> + +#include <stdlib.h> +#include <ctype.h> +#include <inttypes.h> + +#include <rtems/cpuuse.h> + +static void CPU_usage_Per_thread_handler( + Thread_Control *the_thread +) +{ + #ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__ + _Timestamp_Set_to_zero( &the_thread->cpu_time_used ); + #else + the_thread->cpu_time_used = 0; + #endif +} + +/* + * External data that is shared by cpu usage code but not declared in .h files. + */ +#ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__ + extern Timestamp_Control CPU_usage_Uptime_at_last_reset; +#else + extern uint32_t CPU_usage_Ticks_at_last_reset; +#endif + +/* + * rtems_cpu_usage_reset + */ +void rtems_cpu_usage_reset( void ) +{ + #ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__ + _TOD_Get_uptime( &CPU_usage_Uptime_at_last_reset ); + _Thread_Time_of_last_context_switch = CPU_usage_Uptime_at_last_reset; + #else + CPU_usage_Ticks_at_last_reset = _Watchdog_Ticks_since_boot; + #endif + + rtems_iterate_over_all_threads(CPU_usage_Per_thread_handler); +} diff --git a/cpukit/libmisc/cpuuse/cpuuse.h b/cpukit/libmisc/cpuuse/cpuuse.h new file mode 100644 index 0000000000..b38927e7c6 --- /dev/null +++ b/cpukit/libmisc/cpuuse/cpuuse.h @@ -0,0 +1,51 @@ +/* cpuuse.h + * + * This include file contains information necessary to utilize + * and install the cpu usage reporting mechanism. + * + * COPYRIGHT (c) 1989-2007. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef __RTEMS_CPUUSE_h +#define __RTEMS_CPUUSE_h + +#include <rtems/bspIo.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * rtems_cpu_usage_report_with_handler + */ + +void rtems_cpu_usage_report_with_plugin( + void *context, + rtems_printk_plugin_t handler +); + +/* + * rtems_cpu_usage_report + */ + +void rtems_cpu_usage_report( void ); + +/* + * rtems_cpu_usage_reset + */ + +void rtems_cpu_usage_reset( void ); + +#ifdef __cplusplus +} +#endif + +#endif +/* end of include file */ diff --git a/cpukit/libmisc/devnull/devnull.c b/cpukit/libmisc/devnull/devnull.c new file mode 100644 index 0000000000..4b22199814 --- /dev/null +++ b/cpukit/libmisc/devnull/devnull.c @@ -0,0 +1,184 @@ +/* /dev/null + * + * Derived from rtems' stub driver. + * + * Author: Ralf Corsepius (corsepiu@faw.uni-ulm.de) + * + * COPYRIGHT (c) 1989-2000. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/devnull.h> +#include <rtems/libio.h> + +/* null_initialize + * + * This routine is the null device driver init routine. + * + * Input parameters: + * major - device major number + * minor - device minor number + * pargp - pointer to parameter block + * + * Output parameters: + * rval - NULL_SUCCESSFUL + */ + +uint32_t NULL_major; +static char initialized; + +rtems_device_driver null_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor __attribute__((unused)), + void *pargp __attribute__((unused)) +) +{ + rtems_device_driver status; + + if ( !initialized ) { + initialized = 1; + + status = rtems_io_register_name( + "/dev/null", + major, + (rtems_device_minor_number) 0 + ); + + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred(status); + + NULL_major = major; + } + + return RTEMS_SUCCESSFUL; +} + +/* null_open + * + * This routine is the null device driver open routine. + * + * Input parameters: + * major - device major number + * minor - device minor number + * pargb - pointer to open parameter block + * + * Output parameters: + * rval - NULL_SUCCESSFUL + */ + +rtems_device_driver null_open( + rtems_device_major_number major __attribute__((unused)), + rtems_device_minor_number minor __attribute__((unused)), + void *pargp __attribute__((unused)) +) +{ + return NULL_SUCCESSFUL; +} + + +/* null_close + * + * This routine is the null device driver close routine. + * + * Input parameters: + * major - device major number + * minor - device minor number + * pargb - pointer to close parameter block + * + * Output parameters: + * rval - NULL_SUCCESSFUL + */ + +rtems_device_driver null_close( + rtems_device_major_number major __attribute__((unused)), + rtems_device_minor_number minor __attribute__((unused)), + void *pargp __attribute__((unused)) +) +{ + return NULL_SUCCESSFUL; +} + + +/* null_read + * + * This routine is the null device driver read routine. + * + * Input parameters: + * major - device major number + * minor - device minor number + * pargp - pointer to read parameter block + * + * Output parameters: + * rval - NULL_SUCCESSFUL + */ + +rtems_device_driver null_read( + rtems_device_major_number major __attribute__((unused)), + rtems_device_minor_number minor __attribute__((unused)), + void *pargp __attribute__((unused)) +) +{ + return NULL_SUCCESSFUL; +} + + +/* null_write + * + * This routine is the null device driver write routine. + * + * Input parameters: + * major - device major number + * minor - device minor number + * pargp - pointer to write parameter block + * + * Output parameters: + * rval - NULL_SUCCESSFUL + */ + +rtems_device_driver null_write( + rtems_device_major_number major __attribute__((unused)), + rtems_device_minor_number minor __attribute__((unused)), + void *pargp +) +{ + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *) pargp; + + if ( rw_args ) + rw_args->bytes_moved = rw_args->count; + + return NULL_SUCCESSFUL; +} + + +/* null_control + * + * This routine is the null device driver control routine. + * + * Input parameters: + * major - device major number + * minor - device minor number + * pargp - pointer to cntrl parameter block + * + * Output parameters: + * rval - NULL_SUCCESSFUL + */ + +rtems_device_driver null_control( + rtems_device_major_number major __attribute__((unused)), + rtems_device_minor_number minor __attribute__((unused)), + void *pargp __attribute__((unused)) +) +{ + return NULL_SUCCESSFUL; +} diff --git a/cpukit/libmisc/devnull/devnull.h b/cpukit/libmisc/devnull/devnull.h new file mode 100644 index 0000000000..4fe90cde6c --- /dev/null +++ b/cpukit/libmisc/devnull/devnull.h @@ -0,0 +1,73 @@ +/* devnull.h + * + * Null device driver, derived from rtems' stub driver. + * + * Author: Ralf Corsepius (corsepiu@faw.uni-ulm.de) + * + * COPYRIGHT (c) 1989-2000. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef _RTEMS_DEVNULL_H +#define _RTEMS_DEVNULL_H + +#include <rtems/io.h> /* rtems_device_driver */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEVNULL_DRIVER_TABLE_ENTRY \ + { null_initialize, null_open, null_close, null_read, \ + null_write, null_control } + +#define NULL_SUCCESSFUL RTEMS_SUCCESSFUL + +rtems_device_driver null_initialize( + rtems_device_major_number, + rtems_device_minor_number, + void * +); + +rtems_device_driver null_open( + rtems_device_major_number, + rtems_device_minor_number, + void * +); + +rtems_device_driver null_close( + rtems_device_major_number, + rtems_device_minor_number, + void * +); + +rtems_device_driver null_read( + rtems_device_major_number, + rtems_device_minor_number, + void * +); + +rtems_device_driver null_write( + rtems_device_major_number, + rtems_device_minor_number, + void * +); + +rtems_device_driver null_control( + rtems_device_major_number, + rtems_device_minor_number, + void * +); + +#ifdef __cplusplus +} +#endif + +#endif +/* end of include file */ diff --git a/cpukit/libmisc/dummy/README b/cpukit/libmisc/dummy/README new file mode 100644 index 0000000000..03bb30834d --- /dev/null +++ b/cpukit/libmisc/dummy/README @@ -0,0 +1,23 @@ +dummy.rel +========= + +A relocatible objects which contains a dummy configuration for RTEMS. + +Helps linking standard c-program code with RTEMS, which shall *not* be run +on a target, such as configure script code fragments generated by autoconf's +AC_TRY_LINK. + +Example: + +tar xzvf somepkg.tar.gz +cd somepkg + +LDFLAGS=/usr/local/rtems/<cpu>-rtems/<bsp>/lib/dummy.rel \ +CC="<cpu>-rtems-gcc \ +-B/usr/local/rtems/<cpu>-rtems/<bsp>/lib/ -specs bsp_specs -qrtems" \ +CC_FOR_BUILD="gcc" \ +configure --host=<cpu>-rtems --build=i686-pc-linux-gnu +make + +History: +Starting dummy.c with a copy of rtems-19990528/c/src/tests/samples/minimum/init.c diff --git a/cpukit/libmisc/dummy/dummy-networking.c b/cpukit/libmisc/dummy/dummy-networking.c new file mode 100644 index 0000000000..77d708d516 --- /dev/null +++ b/cpukit/libmisc/dummy/dummy-networking.c @@ -0,0 +1,44 @@ +/* + * Dummy configuration file + * + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> + +/* Loopback Network Configuration */ +#if defined(RTEMS_NETWORKING) + #include <rtems/rtems_bsdnet.h> + #include <sys/socket.h> + #include <netinet/in.h> + + struct rtems_bsdnet_config rtems_bsdnet_config = { + NULL, /* Network interface */ + NULL, /* Use fixed network configuration */ + 0, /* Default network task priority */ + 0, /* Default mbuf capacity */ + 0, /* Default mbuf cluster capacity */ + "testSystem", /* Host name */ + "nowhere.com", /* Domain name */ + "127.0.0.1", /* Gateway */ + "127.0.0.1", /* Log host */ + {"127.0.0.1" }, /* Name server(s) */ + {"127.0.0.1" }, /* NTP server(s) */ + 1, /* sb_efficiency */ + 0, /* udp_tx_buf_size */ + 0, /* udp_rx_buf_size */ + 0, /* tcp_tx_buf_size */ + 0 /* tcp_rx_buf_size */ + }; +#endif diff --git a/cpukit/libmisc/dummy/dummy.c b/cpukit/libmisc/dummy/dummy.c new file mode 100644 index 0000000000..b062c06091 --- /dev/null +++ b/cpukit/libmisc/dummy/dummy.c @@ -0,0 +1,38 @@ +/* + * Dummy configuration file + * + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> + +int main( int, char **, char **); + +/* configuration information */ + +/* This is enough to get a basic main() up. */ +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE +#define CONFIGURE_MAXIMUM_TASKS 10 +#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM +#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 20 +#define CONFIGURE_INIT_TASK_ENTRY_POINT (void *)main + +/* Include basic device drivers needed to call delays */ +#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER +#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER + +#define CONFIGURE_INIT + +#include <rtems/confdefs.h> + diff --git a/cpukit/libmisc/dumpbuf/dumpbuf.c b/cpukit/libmisc/dumpbuf/dumpbuf.c new file mode 100644 index 0000000000..97274ecbaa --- /dev/null +++ b/cpukit/libmisc/dumpbuf/dumpbuf.c @@ -0,0 +1,80 @@ +/* + * COPYRIGHT (c) 1997-2007. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may in + * the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <rtems/dumpbuf.h> +#include <rtems/bspIo.h> + +/* + * Put the body below rtems_print_buffer so it won't get inlined. + */ + +static inline void Dump_Line( + const unsigned char *buffer, + int length +); + +void rtems_print_buffer( + const unsigned char *buffer, + int length +) +{ + + int i, mod, max; + + if ( !length ) return; + + mod = length % 16; + + max = length - mod; + + for ( i=0 ; i<max ; i+=16 ) + Dump_Line( &buffer[ i ], 16 ); + + if ( mod ) + Dump_Line( &buffer[ max ], mod ); +} + +static inline void Dump_Line( + const unsigned char *buffer, + int length +) +{ + + int i; + char line_buffer[120]; + + line_buffer[0] = '\0'; + + for( i=0 ; i<length ; i++ ) + sprintf( line_buffer, "%s%02x ", line_buffer, buffer[ i ] ); + + for( ; i<16 ; i++ ) + strcat( line_buffer, " " ); + + strcat( line_buffer, "|" ); + for( i=0 ; i<length ; i++ ) + sprintf( line_buffer, "%s%c", line_buffer, + isprint( buffer[ i ] ) ? buffer[ i ] : '.' ); + + for( ; i<16 ; i++ ) + strcat( line_buffer, " " ); + + strcat( line_buffer, "|\n" ); + + printk( line_buffer ); +} diff --git a/cpukit/libmisc/dumpbuf/dumpbuf.h b/cpukit/libmisc/dumpbuf/dumpbuf.h new file mode 100644 index 0000000000..3502ace70a --- /dev/null +++ b/cpukit/libmisc/dumpbuf/dumpbuf.h @@ -0,0 +1,39 @@ +/* + * COPYRIGHT (c) 1997-2007. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may in + * the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef __DUMP_BUFFER_h +#define __DUMP_BUFFER_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Print Memory Buffer + * + * This method prints @a length bytes beginning at @a buffer in + * a nice format similar to what one would expect from a debugger + * or ROM monitor. + * + * @param[in] buffer is the address of the buffer + * @param[in] length is the length of the buffer + */ +void rtems_print_buffer( + const unsigned char *buffer, + int length +); + +#ifdef __cplusplus +} +#endif + +#endif +/* end of include file */ diff --git a/cpukit/libmisc/fb/fb.h b/cpukit/libmisc/fb/fb.h new file mode 100644 index 0000000000..cf49d7bd91 --- /dev/null +++ b/cpukit/libmisc/fb/fb.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2000 - Rosimildo da Silva + * + * MODULE DESCRIPTION: + * Micro FrameBuffer interface for Embedded Systems. + * + * $Id$ + */ + +#ifndef _MW_FB_H +#define _MW_FB_H + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* ioctls + 0x46 is 'F' */ +#define FBIOGET_VSCREENINFO 0x4600 +#define FBIOPUT_VSCREENINFO 0x4601 +#define FBIOGET_FSCREENINFO 0x4602 +#define FBIOGETCMAP 0x4604 +#define FBIOPUTCMAP 0x4605 +#define FB_EXEC_FUNCTION 0x4606 +#define FBIOSWAPBUFFERS 0x4607 +#define FBIOSETBUFFERMODE 0x4608 + +#define FB_SINGLE_BUFFERED 0 +#define FB_TRIPLE_BUFFERED 1 + +#define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ +#define FB_TYPE_PLANES 1 /* Non interleaved planes */ +#define FB_TYPE_INTERLEAVED_PLANES 2 /* Interleaved planes */ +#define FB_TYPE_TEXT 3 /* Text/attributes */ +#define FB_TYPE_VGA_PLANES 4 /* EGA/VGA planes */ +#define FB_TYPE_VIRTUAL_BUFFER 5 /* Virtual Buffer */ + + +#define FB_VISUAL_MONO01 0 /* Monochr. 1=Black 0=White */ +#define FB_VISUAL_MONO10 1 /* Monochr. 1=White 0=Black */ +#define FB_VISUAL_TRUECOLOR 2 /* True color */ +#define FB_VISUAL_PSEUDOCOLOR 3 /* Pseudo color (like atari) */ +#define FB_VISUAL_DIRECTCOLOR 4 /* Direct color */ +#define FB_VISUAL_STATIC_PSEUDOCOLOR 5 /* Pseudo color readonly */ + +#define FB_ACCEL_NONE 0 /* no hardware accelerator */ + +struct fb_bitfield { + uint32_t offset; /* beginning of bitfield */ + uint32_t length; /* length of bitfield */ + uint32_t msb_right; /* != 0 : Most significant bit is */ + /* right */ +}; + +struct fb_var_screeninfo { + uint32_t xres; /* visible resolution */ + uint32_t yres; + uint32_t bits_per_pixel; /* guess what */ + struct fb_bitfield red; /* bitfield in fb mem if true color, */ + struct fb_bitfield green; /* else only length is significant */ + struct fb_bitfield blue; + struct fb_bitfield transp; /* transparency */ +}; + +struct fb_fix_screeninfo { + volatile char *smem_start; /* Start of frame buffer mem */ + /* (physical address) */ + uint32_t smem_len; /* Length of frame buffer mem */ + uint32_t type; /* see FB_TYPE_* */ + uint32_t visual; /* see FB_VISUAL_* */ + uint32_t line_length; /* number of chars per line */ +}; + +struct fb_cmap { + uint32_t start; /* First entry */ + uint32_t len; /* Number of entries */ + uint16_t *red; /* Red values */ + uint16_t *green; + uint16_t *blue; + uint16_t *transp; /* transparency, can be NULL */ +}; + + + +#ifdef __cplusplus +} +#endif + +#endif /* _MW_FB_H */ diff --git a/cpukit/libmisc/fb/mw_print.c b/cpukit/libmisc/fb/mw_print.c new file mode 100644 index 0000000000..4e8abf2bd8 --- /dev/null +++ b/cpukit/libmisc/fb/mw_print.c @@ -0,0 +1,92 @@ +/* + * COPYRIGHT (c) 1989-2011. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/mw_uid.h> +#include <stdio.h> + +const char *uid_buttons( + unsigned short btns, + char *buffer, + size_t max +) +{ + snprintf( + buffer, + max, + "LEFT=%s CENTER=%s RIGHT=%s", + ((btns & MV_BUTTON_LEFT) ? "down" : "up"), + ((btns & MV_BUTTON_CENTER) ? "down" : "up"), + ((btns & MV_BUTTON_RIGHT) ? "down" : "up") + ); + return buffer; +} + +void uid_print_message( + struct MW_UID_MESSAGE *uid +) +{ + uid_print_message_with_plugin( NULL, printk_plugin, uid ); +} + +void uid_print_message_with_plugin( + void *context, + rtems_printk_plugin_t handler, + struct MW_UID_MESSAGE *uid +) +{ + char buttons[80]; + + switch (uid->type) { + case MV_UID_INVALID: + (*handler)( context, "MV_UID_INVALID\n" ); + break; + case MV_UID_REL_POS: + (*handler)( + context, + "MV_UID_REL_POS - %s x=%d y=%d z=%d\n", + uid_buttons( uid->m.pos.btns, buttons, sizeof(buttons)), + uid->m.pos.x, /* x location */ + uid->m.pos.y, /* y location */ + uid->m.pos.z /* z location, 0 for 2D */ + ); + break; + case MV_UID_ABS_POS: + (*handler)( + context, + "MV_UID_ABS_POS - %s x=%d y=%d z=%d\n", + uid_buttons( uid->m.pos.btns, buttons, sizeof(buttons)), + uid->m.pos.x, /* x location */ + uid->m.pos.y, /* y location */ + uid->m.pos.z /* z location, 0 for 2D */ + ); + break; + case MV_UID_KBD: + (*handler)( context, + "MV_UID_KBD - code=0x%04x modifiers=0x%02x mode=0x%02x\n", + uid->m.kbd.code, /* keycode or scancode */ + uid->m.kbd.modifiers, /* key modifiers */ + uid->m.kbd.mode /* current Kbd mode */ + ); + break; + case MV_UID_TIMER: + (*handler)( context, "MV_UID_TIMER\n" ); + break; + default: + (*handler)( context, "Invalid device type\n" ); + break; + } + +} + diff --git a/cpukit/libmisc/fb/mw_uid.c b/cpukit/libmisc/fb/mw_uid.c new file mode 100644 index 0000000000..e3cf34f276 --- /dev/null +++ b/cpukit/libmisc/fb/mw_uid.c @@ -0,0 +1,150 @@ +/* + * + * Copyright (c) 2000 - Rosimildo da Silva + * + * MODULE DESCRIPTION: + * This module implements the input devices interface used by MicroWindows + * in an embedded system environment. It uses the RTEMS message queue as + * the repository for the messages posted by the devices registered. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <rtems.h> + +#include <rtems/mw_uid.h> +#include <rtems/seterr.h> + +static rtems_id queue_id = 0; +static int open_count = 0; + +/* +#define MW_DEBUG_ON 1 +*/ + +/* open a message queue with the kernel */ +int uid_open_queue( + const char *q_name, + int flags __attribute__((unused)), + size_t max_msgs +) +{ + rtems_status_code status; + + /* + * For the first device calling this function we would create the queue. + * It is assumed that this call is done at initialization, and no concerns + * regarding multi-threading is taken in consideration here. + */ + if ( open_count ) { + open_count++; + return 0; + } + + status = rtems_message_queue_create( + rtems_build_name( q_name[0], q_name[1], q_name[2], q_name[3] ), + max_msgs, + sizeof( struct MW_UID_MESSAGE ), + RTEMS_FIFO | RTEMS_LOCAL, + &queue_id + ); + if ( status != RTEMS_SUCCESSFUL ) { + #ifdef MW_DEBUG_ON + printk( "UID_Queue: error creating queue: %d\n", status ); + #endif + return -1; + } + #ifdef MW_DEBUG_ON + printk( "UID_Queue: id=%X\n", queue_id ); + #endif + open_count++; + return 0; +} + + +/* close message queue */ +int uid_close_queue( void ) +{ + if ( open_count == 1 ) { + rtems_message_queue_delete( queue_id ); + queue_id = 0; + } + open_count--; + return 0; +} + +/* reads for a message from the device */ +int uid_read_message( struct MW_UID_MESSAGE *m, unsigned long timeout ) +{ + rtems_status_code status; + size_t size = 0; + unsigned long micro_secs = timeout*1000; + int wait = (timeout != 0); + + status = rtems_message_queue_receive( + queue_id, + (void*)m, + &size, + wait ? RTEMS_WAIT : RTEMS_NO_WAIT, + RTEMS_MICROSECONDS_TO_TICKS(micro_secs) + ); + + if( status == RTEMS_SUCCESSFUL ) { + return size; + } else if( ( status == RTEMS_UNSATISFIED ) || ( status == RTEMS_TIMEOUT ) ) { + rtems_set_errno_and_return_minus_one( ETIMEDOUT ); + } + /* Here we have one error condition */ + #ifdef MW_DEBUG_ON + printk( "UID_Queue: error reading queue: %d\n", status ); + #endif + return -1; +} + +/* + * add a message to the queue of events. This method can be used to + * simulate hardware events, and it can be very handy during development + * a new interface. + */ +int uid_send_message( struct MW_UID_MESSAGE *m ) +{ + rtems_status_code status; + status = rtems_message_queue_send( + queue_id, ( void * )m, sizeof( struct MW_UID_MESSAGE ) ); + return (status == RTEMS_SUCCESSFUL) ? 0 : -1; +} + +/* + * register the device to insert events to the message + * queue named as the value passed in q_name + */ +int uid_register_device( int fd, const char *q_name ) +{ + return ioctl( fd, MW_UID_REGISTER_DEVICE, q_name ); +} + +/* tell this device to stop adding events to the queue */ +int uid_unregister_device( int fd ) +{ + return ioctl( fd, MW_UID_UNREGISTER_DEVICE, NULL ); +} + +/* set the keyboard */ +int uid_set_kbd_mode( int fd, int mode, int *old_mode ) +{ + if (ioctl( fd, MV_KDGKBMODE, old_mode) < 0) { + return -1; + } + if (ioctl(fd, MV_KDSKBMODE, mode ) < 0 ) { + return -1; + } + return 0; +} diff --git a/cpukit/libmisc/fb/mw_uid.h b/cpukit/libmisc/fb/mw_uid.h new file mode 100644 index 0000000000..89f440acf4 --- /dev/null +++ b/cpukit/libmisc/fb/mw_uid.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2000 - Rosimildo da Silva + * + * MODULE DESCRIPTION: + * This module defines the interface for input devices used by MicroWindows + * in an embedded system environment. + * + * $Id$ + */ + +#ifndef _MW_UID_H +#define _MW_UID_H + +#include <sys/types.h> +#include <rtems/bspIo.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* 0x41XX -- IOCTL functions for the Micro Input Devices commands */ +#define MW_UID_REGISTER_DEVICE 0x4100 +#define MW_UID_UNREGISTER_DEVICE 0x4101 + +/* devices supported by MicroWindows */ +enum MW_INPUT_DEVICE_TYPE { + MV_UID_INVALID = 0, + MV_UID_REL_POS = 1, /* mouse */ + MV_UID_ABS_POS = 2, /* touch-screen */ + MV_UID_KBD = 3, /* keyboard */ + MV_UID_TIMER = 4 /* timer -- not used */ +}; + +/* matching MicroWindows */ +#define MV_BUTTON_RIGHT 0x01 +#define MV_BUTTON_CENTER 0x02 +#define MV_BUTTON_LEFT 0x04 + +/* modifiers of the keyboard type devices */ +#define MV_KEY_MODIFIER_SHIFT_DOWN 0x10 +#define MV_KEY_MODIFIER_ALT_DOWN 0x20 + +/* indication of the LEDS */ +#define MV_KEY_MODIFIER_CAPS_ON 0x04 +#define MV_KEY_MODIFIER_NUN_LOCK_ON 0x02 +#define MV_KEY_SCROLL_LOCK_ON 0x01 + +/* keyboard modes -- default ASCII */ +#define MV_KEY_MODE_ASCII 0x01 +/* + * This mode one event is sent when a key is pressed, + * and another one is send when a key is released. + */ +#define MV_KEY_MODE_SCANCODE 0x00 + +/* these defines match with the linux keyboard range + * for ioctls functions for the keyboard interface. + * 0x4BXX --- keyboard related functions + */ +#define MV_KDGKBMODE 0x4B44 /* gets current keyboard mode */ +#define MV_KDSKBMODE 0x4B45 /* sets current keyboard mode */ + +/* + * Message generated by input devices controlled by MicroWindows. + */ +struct MW_UID_MESSAGE { + enum MW_INPUT_DEVICE_TYPE type; /* device type */ + union { + /* fired when keyboard events are raised */ + struct kbd_t { + unsigned short code; /* keycode or scancode */ + unsigned char modifiers; /* key modifiers */ + unsigned char mode; /* current Kbd mode */ + } kbd; + + /* fired when position events are raised, mouse, touch screen, etc */ + struct pos_t { + unsigned short btns; /* indicates which buttons are pressed */ + short x; /* x location */ + short y; /* y location */ + short z; /* z location, 0 for 2D */ + } pos; + + /* fired by a timer device periodically */ + struct timer_t { + unsigned long frt; /* free running timer */ + unsigned long seq; /* sequence number */ + } tmr; + } m; +}; + + +/* + * API for creating/closing/accessing the message queue used by the micro + * input device interface. All functions in this interface returns a + * zero ( 0 ) on success. One exception for that is the "read" routine + * that returns the number of bytes read. Negaive numbers indicate errors + * + * The implementation of the message queue for RTEMS uses a POSIX message + * queue interface. It should be very portable among systems with a POSIX + * support. + */ + +/** + * This method creates the message queue that holds events from the + * input devices. + * + * @param[in] q_name is the name of the message queue + * @param[in] flags controls the behaviour of the queue + * @param[in] max_msgs specifies the maximum number of pending messages + * + * @note The message queue is from the Classic API. + * + * @return This method returns 0 on success and -1 on error. + */ +extern int uid_open_queue( const char *q_name, int flags, size_t max_msgs ); + +/** + * This method closes the message queue and deletes it. + * + * @return This method returns 0 on success and -1 on error. + */ +extern int uid_close_queue( void ); + +/** + * This method reads a message from the queue. It waits up to the specified + * timeout in miliseconds. A @a timeout of 0 is a poll. + * + * @param[in] m will be filled in with the received message + * @param[in] timeout is the maximum number of mulliseconds to wait + * + * @return This method returns 0 on success and -1 on error. + */ +extern int uid_read_message( struct MW_UID_MESSAGE *m, unsigned long timeout ); + +/** + * This methods writes a message to the queue. + * + * @param[in] m is the message to send + * + * @return This method returns 0 on success and -1 on error. + */ +extern int uid_send_message( struct MW_UID_MESSAGE *m ); + +/** + * This method registers the device associated with @a fd to + * to insert data to the queue + */ +extern int uid_register_device( int fd, const char *q_name ); + +/* unregister device to stop adding messages to the queue */ +extern int uid_unregister_device( int fd ); + +/* set the keyboard */ +extern int uid_set_kbd_mode( int fd, int mode, int *old_mode ); + +/** + * This methods prints the specified UID message using printk + * + * @param[in] uid points to the message to print + */ +void uid_print_message( + struct MW_UID_MESSAGE *uid +); + +/** + * This methods prints the specified UID message using your fprintf + * style method of choice. + * + * @param[in] context is a pointer to a data area which may be + * used by some print handlers + * @param[in] handler is the fprintf style method to invoke + * @param[in] uid points to the message to print + */ +void uid_print_message_with_plugin( + void *context, + rtems_printk_plugin_t handler, + struct MW_UID_MESSAGE *uid +); + +#ifdef __cplusplus +} +#endif + +#endif /* _MW_UID_H */ diff --git a/cpukit/libmisc/fsmount/README b/cpukit/libmisc/fsmount/README new file mode 100644 index 0000000000..88bfda62e7 --- /dev/null +++ b/cpukit/libmisc/fsmount/README @@ -0,0 +1,24 @@ +# +# +# fsmount information +# +# Author: Thomas Doerfler 02/07/2003 +# +# README,v 1.1 1999/07/09 17:23:15 joel Exp +# + +fsmount.c contains the function fsmount. It processes the +fs table given as an parameter to create the listed mount points +and mount the corresponding file systems to their mount points. +See "c/src/tests/samples/fileio" for a sample on how to use this +function. + +The field "report_reasons" specifies, what results of the mount point +creation/mount operations should print to the console. + +The field "abort_reasons" specifies, what results of the mount point +creation/mount operations should abort the function. Do not set the +"abort_reasons" bit, unless you want to stop the processing of the +fsmount table after the first successful mount. + + diff --git a/cpukit/libmisc/fsmount/fsmount.c b/cpukit/libmisc/fsmount/fsmount.c new file mode 100644 index 0000000000..1d82b94391 --- /dev/null +++ b/cpukit/libmisc/fsmount/fsmount.c @@ -0,0 +1,138 @@ +/** + * @file + * + * File system mount functions. + */ + +/*===============================================================*\ +| Project: RTEMS fsmount | ++-----------------------------------------------------------------+ +| File: fsmount.c | ++-----------------------------------------------------------------+ +| Copyright (c) 2003 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file contains the fsmount functions. These functions | +| are used to mount a list of filesystems (and create their mount | +| points before) | +| | +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| http://www.rtems.com/license/LICENSE. | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 02.07.03 creation doe | +\*===============================================================*/ + +#ifndef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/fsmount.h> +#include <rtems/libio.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int rtems_fsmount +( +/*-------------------------------------------------------------------------*\ + | Purpose: | + | This function will create the mount points listed and mount the file | + | systems listed in the calling parameters | + +---------------------------------------------------------------------------+ + | Input Parameters: | + \*-------------------------------------------------------------------------*/ + const rtems_fstab_entry *fstab_ptr, + size_t fstab_count, + size_t *fail_idx + ) +/*-------------------------------------------------------------------------*\ + | Return Value: | + | 0, if success, -1 and errno if failed | + \*=========================================================================*/ +{ + int rc = 0; + int tmp_rc; + size_t fstab_idx = 0; + bool terminate = false; + + /* + * scan through all fstab entries; + */ + while (!terminate && + (fstab_idx < fstab_count)) { + tmp_rc = 0; + /* + * create mount point + */ + if (tmp_rc == 0) { + tmp_rc = rtems_mkdir(fstab_ptr->target, S_IRWXU | S_IRWXG | S_IRWXO); + if (tmp_rc != 0) { + if (0 != (fstab_ptr->report_reasons & FSMOUNT_MNTPNT_CRTERR)) { + fprintf(stdout,"fsmount: creation of mount point \"%s\" failed: %s\n", + fstab_ptr->target, + strerror(errno)); + } + if (0 != (fstab_ptr->abort_reasons & FSMOUNT_MNTPNT_CRTERR)) { + terminate = true; + rc = tmp_rc; + } + } + } + /* + * mount device to given mount point + */ + if (tmp_rc == 0) { + tmp_rc = mount(fstab_ptr->source, + fstab_ptr->target, + fstab_ptr->type, + fstab_ptr->options, + NULL); + if (tmp_rc != 0) { + if (0 != (fstab_ptr->report_reasons & FSMOUNT_MNT_FAILED)) { + fprintf(stdout,"fsmount: mounting of \"%s\" to" + " \"%s\" failed: %s\n", + fstab_ptr->source, + fstab_ptr->target, + strerror(errno)); + } + if (0 != (fstab_ptr->abort_reasons & FSMOUNT_MNT_FAILED)) { + terminate = true; + rc = tmp_rc; + } + } + else { + if (0 != (fstab_ptr->report_reasons & FSMOUNT_MNT_OK)) { + fprintf(stdout,"fsmount: mounting of \"%s\" to" + " \"%s\" succeeded\n", + fstab_ptr->source, + fstab_ptr->target); + } + if (0 != (fstab_ptr->abort_reasons & FSMOUNT_MNT_OK)) { + terminate = true; + } + } + } + /* + * proceed to next entry + */ + if (!terminate) { + fstab_ptr++; + fstab_idx++; + } + } + if (fail_idx != NULL) { + *fail_idx = fstab_idx; + } + return rc; +} diff --git a/cpukit/libmisc/fsmount/fsmount.h b/cpukit/libmisc/fsmount/fsmount.h new file mode 100644 index 0000000000..94c8508358 --- /dev/null +++ b/cpukit/libmisc/fsmount/fsmount.h @@ -0,0 +1,209 @@ +/** + * @file + * + * File system mount functions. + */ + +/*===============================================================*\ +| Project: RTEMS fsmount | ++-----------------------------------------------------------------+ +| File: fsmount.h | ++-----------------------------------------------------------------+ +| Copyright (c) 2003 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file contains the fsmount functions. These functions | +| are used to mount a list of filesystems (and create their mount | +| points before) | +| | +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| http://www.rtems.com/license/LICENSE. | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 02.07.03 creation doe | +\*===============================================================*/ + +#ifndef _FSMOUNT_H +#define _FSMOUNT_H + +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/libcsupport.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup rtems_fstab File System Mount Support + * + * @{ + */ + +/** + * File system mount report and abort condition flags. + * + * The flags define, which conditions will cause a report during the mount + * process (via printf()) or abort the mount process. + * + * @see rtems_fstab_entry and rtems_fsmount(). + */ +typedef enum { + /** + * No conditions. + */ + RTEMS_FSTAB_NONE = 0U, + + /** + * Complete mount process was successful. + */ + RTEMS_FSTAB_OK = 0x1U, + + /** + * Mount point creation failed. + */ + RTEMS_FSTAB_ERROR_MOUNT_POINT = 0x2U, + + /** + * File system mount failed. + */ + RTEMS_FSTAB_ERROR_MOUNT = 0x4U, + + /** + * Something failed. + */ + RTEMS_FSTAB_ERROR = RTEMS_FSTAB_ERROR_MOUNT_POINT | RTEMS_FSTAB_ERROR_MOUNT, + + /** + * Any condition. + */ + RTEMS_FSTAB_ANY = RTEMS_FSTAB_OK | RTEMS_FSTAB_ERROR +} rtems_fstab_conditions; + +/** + * File system table entry. + */ +typedef struct { + /** + * Source for the mount. + */ + const char *source; + + /** + * Target for the mount. + */ + const char *target; + + /** + * File system operations. + */ + const char *type; + + /** + * File system mount options. + */ + rtems_filesystem_options_t options; + + /** + * Report @ref rtems_fstab_conditions "condition flags". + */ + uint16_t report_reasons; + + /** + * Abort @ref rtems_fstab_conditions "condition flags". + */ + uint16_t abort_reasons; +} rtems_fstab_entry; + +/** + * Mounts the file systems listed in the file system mount table @a fstab of + * size @a size. + * + * Each file system will be mounted according to its table entry parameters. + * In case of an abort condition the corresponding table index will be reported + * in @a abort_index. The pointer @a abort_index may be @c NULL. The mount + * point paths will be created with rtems_mkdir() and need not exist + * beforehand. + * + * On success, zero is returned. On error, -1 is returned, and @c errno is set + * appropriately. + * + * @see rtems_bdpart_register_from_disk(). + * + * The following example code tries to mount a FAT file system within a SD + * Card. Some cards do not have a partition table so at first it tries to find + * a file system inside the hole disk. If this is successful the mount process + * will be aborted because the @ref RTEMS_FSTAB_OK condition is true. If this + * did not work it tries to mount the file system inside the first partition. + * If this fails the mount process will not be aborted (this is already the + * last entry), but the last error status will be returned. + * + * @code + * #include <stdio.h> + * #include <string.h> + * #include <errno.h> + * + * #include <rtems.h> + * #include <rtems/bdpart.h> + * #include <rtems/error.h> + * #include <rtems/fsmount.h> + * + * static const rtems_fstab_entry fstab [] = { + * { + * .source = "/dev/sd-card-a", + * .target = "/mnt", + * .type = "dosfs", + * .options = RTEMS_FILESYSTEM_READ_WRITE, + * .report_reasons = RTEMS_FSTAB_ANY, + * .abort_reasons = RTEMS_FSTAB_OK + * }, { + * .source = "/dev/sd-card-a1", + * .target = "/mnt", + * .type = "dosfs", + * .options = RTEMS_FILESYSTEM_READ_WRITE, + * .report_reasons = RTEMS_FSTAB_ANY, + * .abort_reasons = RTEMS_FSTAB_NONE + * } + * }; + * + * static void my_mount(void) + * { + * rtems_status_code sc = RTEMS_SUCCESSFUL; + * int rv = 0; + * size_t abort_index = 0; + * + * sc = rtems_bdpart_register_from_disk("/dev/sd-card-a"); + * if (sc != RTEMS_SUCCESSFUL) { + * printf("read partition table failed: %s\n", rtems_status_text(sc)); + * } + * + * rv = rtems_fsmount(fstab, sizeof(fstab) / sizeof(fstab [0]), &abort_index); + * if (rv != 0) { + * printf("mount failed: %s\n", strerror(errno)); + * } + * printf("mount aborted at %zu\n", abort_index); + * } + * @endcode + */ +int rtems_fsmount( const rtems_fstab_entry *fstab, size_t size, size_t *abort_index); + +/** @} */ + +typedef rtems_fstab_entry fstab_t; + +#define FSMOUNT_MNT_OK RTEMS_FSTAB_OK + +#define FSMOUNT_MNTPNT_CRTERR RTEMS_FSTAB_ERROR_MOUNT_POINT + +#define FSMOUNT_MNT_FAILED RTEMS_FSTAB_ERROR_MOUNT + +#ifdef __cplusplus +} +#endif + +#endif /* _FSMOUNT_H */ diff --git a/cpukit/libmisc/monitor/README b/cpukit/libmisc/monitor/README new file mode 100644 index 0000000000..d5a73da140 --- /dev/null +++ b/cpukit/libmisc/monitor/README @@ -0,0 +1,97 @@ +# +# $Id$ +# + +monitor task + +The monitor task is an optional task that knows about RTEMS +data structures and can print out information about them. +It is a work-in-progress and needs many more commands, but +is useful now. + +The monitor works best when it is the highest priority task, +so all your other tasks should ideally be at some priority +greater than 1. + +To use the monitor: +------------------- + + #include <rtems/monitor.h> + + ... + + rtems_monitor_init(0); + + The parameter to rtems_monitor_init() tells the monitor whether + to suspend itself on startup. A value of 0 causes the monitor + to immediately enter command mode; a non-zero value causes the + monitor to suspend itself after creation and wait for explicit + wakeup. + + + rtems_monitor_wakeup(); + + wakes up a suspended monitor and causes it to reenter command mode. + +Monitor commands +---------------- + + The monitor prompt is 'rtems> '. + Can abbreviate commands to "uniquity" + There is a 'help' command. Here is the output from various + help commands: + + Commands (may be abbreviated) + + help -- get this message or command specific help + task -- show task information + queue -- show message queue information + symbol -- show entries from symbol table + pause -- pause monitor for a specified number of ticks + fatal -- invoke a fatal RTEMS error + + task [id [id ...] ] + display information about the specified tasks. + Default is to display information about all tasks on this node + + queue [id [id ... ] ] + display information about the specified message queues + Default is to display information about all queues on this node + + symbol [ symbolname [symbolname ... ] ] + display value associated with specified symbol. + Defaults to displaying all known symbols. + + pause [ticks] + monitor goes to "sleep" for specified ticks (default is 1) + monitor will resume at end of period or if explicitly awakened + + fatal [status] + Invoke 'rtems_fatal_error_occurred' with 'status' + (default is RTEMS_INTERNAL_ERROR) + + continue + put the monitor to sleep waiting for an explicit wakeup from the + program running. + + +Sample output from 'task' command +--------------------------------- + + rtems> task + ID NAME PRIO STAT MODES EVENTS WAITID WAITARG NOTES + ------------------------------------------------------------------------ + 00010001 UI1 2 READY P:T:nA NONE15: 0x40606348 + 00010002 RMON 1 READY nP NONE15: 0x40604110 + + 'RMON' is the monitor itself, so we have 1 "user" task. + Its modes are P:T:nA which translate to: + + preemptable + timesliced + no ASRS + + It has no events. + It has a notepad value for notepad 15 which is 0x40606348 + (this is the libc thread state) + diff --git a/cpukit/libmisc/monitor/mon-command.c b/cpukit/libmisc/monitor/mon-command.c new file mode 100644 index 0000000000..b16c8f06c0 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-command.c @@ -0,0 +1,244 @@ +/** + * @file + * + * @brief Command support routines for RTEMS monitor. + */ + +/* + * $Id$ + * + * 2001-01-30 KJO (vac4050@cae597.rsc.raytheon.com): + * Fixed rtems_monitor_command_lookup() to accept partial + * commands to uniqeness. Added support for setting + * the monitor prompt via an environment variable: + * RTEMS_MONITOR_PROMPT + * + * CCJ: 26-3-2000, adding command history and command line + * editing. This code is donated from My Right Boot and not + * covered by GPL, only the RTEMS license. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <stdio.h> + +#include <rtems.h> +#include <rtems/monitor.h> + +static void +rtems_monitor_show_help ( + const rtems_monitor_command_entry_t *help_cmd, + int max_cmd_len +) +{ +#define MAX_HELP_LINE_LENGTH (75 - max_cmd_len - 2) + + if (help_cmd && help_cmd->command) + { + const char *help = help_cmd->usage; + int help_len = strlen (help); + int spaces = max_cmd_len - strlen (help_cmd->command); + int show_this_line = 0; + int line_one = 1; + int c; + + fprintf(stdout,"%s", help_cmd->command); + + if (help_len == 0) + { + fprintf(stdout," - No help associated.\n"); + return; + } + + while (help_len) + { + fprintf(stdout,"%*c", spaces, ' '); + + if (line_one) + fprintf(stdout," - "); + + spaces = max_cmd_len + 2; + line_one = 0; + + /* + * See if greater then the line length if so, work back + * from the end for a space, tab or lf or cr. + */ + + if (help_len > MAX_HELP_LINE_LENGTH) + { + for (show_this_line = MAX_HELP_LINE_LENGTH - 1; + show_this_line; + show_this_line--) + if ((help[show_this_line] == ' ') || + (help[show_this_line] == '\n') || + (help[show_this_line] == '\r')) + break; + + /* + * If show_this_line is 0, it is a very long word !! + */ + + if (show_this_line == 0) + show_this_line = MAX_HELP_LINE_LENGTH - 1; + } + else + show_this_line = help_len; + + for (c = 0; c < show_this_line; c++) + if ((help[c] == '\r') || (help[c] == '\n')) + show_this_line = c; + else + putchar (help[c]); + + fprintf(stdout,"\n"); + + help += show_this_line; + help_len -= show_this_line; + + /* + * Move past the line feeds or what ever else is being skipped. + */ + + while (help_len) + { + if ((*help != '\r') && (*help != '\n')) + break; + + if (*help != ' ') + { + help++; + help_len--; + break; + } + help++; + help_len--; + } + } + } +} + +void +rtems_monitor_command_usage( + const rtems_monitor_command_entry_t *table, + const char *command_name +) +{ + const rtems_monitor_command_entry_t *command = table; + int max_cmd_len = 0; + + /* if first entry in table is a usage, then print it out */ + + if (command_name && (*command_name != '\0')) + { + command = rtems_monitor_command_lookup (command_name); + + if (command) + rtems_monitor_show_help (command, strlen (command_name)); + else + fprintf(stdout,"Unrecognised command; try just 'help'\n"); + return; + } + + /* + * Find the largest command size. + */ + + while (command) + { + int len = command->command ? strlen (command->command) : 0 ; + + if (len > max_cmd_len) + max_cmd_len = len; + + command = command->next; + } + + max_cmd_len++; + + command = table; + + /* + * Now some nice formatting for the help. + */ + + while (command) + { + rtems_monitor_show_help (command, max_cmd_len); + command = command->next; + } +} + + +void rtems_monitor_help_cmd( + int argc, + char **argv, + const rtems_monitor_command_arg_t *command_arg, + bool verbose __attribute__((unused)) +) +{ + int arg; + const rtems_monitor_command_entry_t *command = + command_arg->monitor_command_entry; + + if (argc == 1) + rtems_monitor_command_usage(command, 0); + else + { + for (arg = 1; argv[arg]; arg++) + rtems_monitor_command_usage(command, argv[arg]); + } +} + +typedef struct { + const char *name; + size_t length; + const rtems_monitor_command_entry_t *match; +} rtems_monitor_command_lookup_entry; + +static bool rtems_monitor_command_lookup_routine( + const rtems_monitor_command_entry_t *e, + void *arg +) +{ + rtems_monitor_command_lookup_entry *le = + (rtems_monitor_command_lookup_entry *) arg; + + /* Check name */ + if (strncmp(e->command, le->name, le->length) == 0) { + /* Check for ambiguity */ + if (le->match == NULL) { + le->match = e; + } else { + return false; + } + } + + return true; +} + +/** + * @brief Looks for a command with the name @a name in the list of registered + * commands. + * + * The parameter @a name must not be NULL. + * + * Returns the corresponding command entry or NULL if no command is found. + */ +const rtems_monitor_command_entry_t *rtems_monitor_command_lookup( + const char *name +) +{ + rtems_monitor_command_lookup_entry e = { + .name = name, + .length = strlen( name), + .match = NULL + }; + + rtems_monitor_command_iterate(rtems_monitor_command_lookup_routine, &e); + + return e.match; +} diff --git a/cpukit/libmisc/monitor/mon-config.c b/cpukit/libmisc/monitor/mon-config.c new file mode 100644 index 0000000000..b067db7e2f --- /dev/null +++ b/cpukit/libmisc/monitor/mon-config.c @@ -0,0 +1,136 @@ +/* + * RTEMS Config display support + * + * TODO + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#include <rtems.h> +#include <rtems/monitor.h> + +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> /* strtoul() */ + +#define DATACOL 15 +#define CONTCOL DATACOL /* continued col */ + +/* + * Fill in entire monitor config table + * for sending to a remote monitor or printing on the local system + */ + +void +rtems_monitor_config_canonical( + rtems_monitor_config_t *canonical_config, + void *config_void +) +{ + rtems_configuration_table *c = (rtems_configuration_table *) config_void; + rtems_api_configuration_table *r = &Configuration_RTEMS_API; + + canonical_config->work_space_start = c->work_space_start; + canonical_config->work_space_size = c->work_space_size; + canonical_config->maximum_tasks = r->maximum_tasks; + canonical_config->maximum_timers = r->maximum_timers; + canonical_config->maximum_semaphores = r->maximum_semaphores; + canonical_config->maximum_message_queues = r->maximum_message_queues; + canonical_config->maximum_partitions = r->maximum_partitions; + canonical_config->maximum_regions = r->maximum_regions; + canonical_config->maximum_ports = r->maximum_ports; + canonical_config->maximum_periods = r->maximum_periods; + canonical_config->maximum_extensions = c->maximum_extensions; + canonical_config->microseconds_per_tick = c->microseconds_per_tick; + canonical_config->ticks_per_timeslice = c->ticks_per_timeslice; + canonical_config->number_of_initialization_tasks = r->number_of_initialization_tasks; +} + +/* + * This is easy, since there is only 1 (altho we could get them from + * other nodes...) + */ + +void * +rtems_monitor_config_next( + void *object_info __attribute__((unused)), + rtems_monitor_config_t *canonical_config __attribute__((unused)), + rtems_id *next_id +) +{ + rtems_configuration_table *c = &Configuration; + int n = rtems_object_id_get_index(*next_id); + + if (n >= 1) + goto failed; + + _Thread_Disable_dispatch(); + + *next_id += 1; + return (void *) c; + +failed: + *next_id = RTEMS_OBJECT_ID_FINAL; + return 0; +} + + +void +rtems_monitor_config_dump_header( + bool verbose __attribute__((unused)) +) +{ + fprintf(stdout,"\ +INITIAL (startup) Configuration Info\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +0 1 2 3 4 5 6 7 */ + rtems_monitor_separator(); +} + + +int +rtems_monitor_config_dump( + rtems_monitor_config_t *monitor_config, + bool verbose __attribute__((unused)) +) +{ + int length = 0; + + length = 0; + length += fprintf(stdout,"WORKSPACE"); + length += rtems_monitor_pad(DATACOL, length); + length += fprintf(stdout,"start: %p; size: 0x%" PRIx32 "\n", + monitor_config->work_space_start, + monitor_config->work_space_size); + + length = 0; + length += fprintf(stdout,"TIME"); + length += rtems_monitor_pad(DATACOL, length); + length += fprintf(stdout,"usec/tick: %" PRId32 "; tick/timeslice: %" PRId32 "; tick/sec: %" PRId32 "\n", + monitor_config->microseconds_per_tick, + monitor_config->ticks_per_timeslice, + 1000000 / monitor_config->microseconds_per_tick); + + length = 0; + length += fprintf(stdout,"MAXIMUMS"); + length += rtems_monitor_pad(DATACOL, length); + length += fprintf(stdout,"tasks: %" PRId32 "; timers: %" PRId32 "; sems: %" PRId32 "; que's: %" PRId32 "; ext's: %" PRId32 "\n", + monitor_config->maximum_tasks, + monitor_config->maximum_timers, + monitor_config->maximum_semaphores, + monitor_config->maximum_message_queues, + monitor_config->maximum_extensions); + length = 0; + length += rtems_monitor_pad(CONTCOL, length); + length += fprintf(stdout,"partitions: %" PRId32 "; regions: %" PRId32 "; ports: %" PRId32 "; periods: %" PRId32 "\n", + monitor_config->maximum_partitions, + monitor_config->maximum_regions, + monitor_config->maximum_ports, + monitor_config->maximum_periods); + return length; +} diff --git a/cpukit/libmisc/monitor/mon-driver.c b/cpukit/libmisc/monitor/mon-driver.c new file mode 100644 index 0000000000..c5c2c468b9 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-driver.c @@ -0,0 +1,144 @@ +/* + * RTEMS monitor IO (device drivers) support + * + * There are 2 "driver" things the monitor knows about. + * + * 1. Regular RTEMS drivers. + * This is a table indexed by major device number and + * containing driver entry points only. + * + * 2. Driver name table. + * A separate table of names for drivers. + * The table converts driver names to a major number + * as index into the driver table and a minor number + * for an argument to driver. + * + * Drivers are displayed with 'driver' command. + * Names are displayed with 'name' command. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#include <rtems.h> + +#include <rtems/monitor.h> + +#include <stdio.h> +#include <stdlib.h> /* strtoul() */ +#include <inttypes.h> + +#define DATACOL 15 +#define CONTCOL DATACOL /* continued col */ + + +void +rtems_monitor_driver_canonical( + rtems_monitor_driver_t *canonical_driver, + void *driver_void +) +{ + rtems_driver_address_table *d = (rtems_driver_address_table *) driver_void; + + rtems_monitor_symbol_canonical_by_value(&canonical_driver->initialization, + (void *) d->initialization_entry); + + rtems_monitor_symbol_canonical_by_value(&canonical_driver->open, + (void *) d->open_entry); + rtems_monitor_symbol_canonical_by_value(&canonical_driver->close, + (void *) d->close_entry); + rtems_monitor_symbol_canonical_by_value(&canonical_driver->read, + (void *) d->read_entry); + rtems_monitor_symbol_canonical_by_value(&canonical_driver->write, + (void *) d->write_entry); + rtems_monitor_symbol_canonical_by_value(&canonical_driver->control, + (void *) d->control_entry); +} + + +void * +rtems_monitor_driver_next( + void *object_info __attribute__((unused)), + rtems_monitor_driver_t *canonical_driver, + rtems_id *next_id +) +{ + rtems_configuration_table *c = &Configuration; + uint32_t n = rtems_object_id_get_index(*next_id); + + if (n >= c->number_of_device_drivers) + goto failed; + + _Thread_Disable_dispatch(); + + /* + * dummy up a fake id and name for this item + */ + + canonical_driver->id = n; + canonical_driver->name = rtems_build_name('-', '-', '-', '-'); + + *next_id += 1; + return (void *) (c->Device_driver_table + n); + +failed: + *next_id = RTEMS_OBJECT_ID_FINAL; + return 0; +} + + +void +rtems_monitor_driver_dump_header( + bool verbose __attribute__((unused)) +) +{ + fprintf(stdout,"\ + Major Entry points\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +0 1 2 3 4 5 6 7 */ + rtems_monitor_separator(); +} + +void +rtems_monitor_driver_dump( + rtems_monitor_driver_t *monitor_driver, + bool verbose +) +{ + uint32_t length = 0; + +#if defined(RTEMS_USE_16_BIT_OBJECT) + length += fprintf(stdout," %" PRId16 "", monitor_driver->id); +#else + length += fprintf(stdout," %" PRId32 "", monitor_driver->id); +#endif + length += rtems_monitor_pad(13, length); + length += fprintf(stdout,"init: "); + length += rtems_monitor_symbol_dump(&monitor_driver->initialization, verbose); + length += fprintf(stdout,"; control: "); + length += rtems_monitor_symbol_dump(&monitor_driver->control, verbose); + length += fprintf(stdout,"\n"); + length = 0; + + length += rtems_monitor_pad(13, length); + + length += fprintf(stdout,"open: "); + length += rtems_monitor_symbol_dump(&monitor_driver->open, verbose); + length += fprintf(stdout,"; close: "); + length += rtems_monitor_symbol_dump(&monitor_driver->close, verbose); + length += fprintf(stdout,"\n"); + length = 0; + + length += rtems_monitor_pad(13, length); + + length += fprintf(stdout,"read: "); + length += rtems_monitor_symbol_dump(&monitor_driver->read, verbose); + length += fprintf(stdout,"; write: "); + length += rtems_monitor_symbol_dump(&monitor_driver->write, verbose); + length += fprintf(stdout,"\n"); + length = 0; +} diff --git a/cpukit/libmisc/monitor/mon-editor.c b/cpukit/libmisc/monitor/mon-editor.c new file mode 100644 index 0000000000..1104b50185 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-editor.c @@ -0,0 +1,643 @@ +/** + * @file + * + * @brief Command line editor for RTEMS monitor. + */ + +/* + * $Id$ + * + * 2001-01-30 KJO (vac4050@cae597.rsc.raytheon.com): + * Fixed rtems_monitor_command_lookup() to accept partial + * commands to uniqeness. Added support for setting + * the monitor prompt via an environment variable: + * RTEMS_MONITOR_PROMPT + * + * CCJ: 26-3-2000, adding command history and command line + * editing. This code is donated from My Right Boot and not + * covered by GPL, only the RTEMS license. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> + +#include <rtems/monitor.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> +#include <termios.h> +#include <unistd.h> + +#ifndef MONITOR_PROMPT +#define MONITOR_PROMPT "rtems" /* will have '> ' appended */ +#endif + +/* + * Some key labels to define special keys. + */ + +#define KEYS_EXTENDED (0x8000) +#define KEYS_NORMAL_MASK (0x00ff) +#define KEYS_INS (0) +#define KEYS_DEL (1) +#define KEYS_UARROW (2) +#define KEYS_DARROW (3) +#define KEYS_LARROW (4) +#define KEYS_RARROW (5) +#define KEYS_HOME (6) +#define KEYS_END (7) +#define KEYS_F1 (8) +#define KEYS_F2 (9) +#define KEYS_F3 (10) +#define KEYS_F4 (11) +#define KEYS_F5 (12) +#define KEYS_F6 (13) +#define KEYS_F7 (14) +#define KEYS_F8 (15) +#define KEYS_F9 (16) +#define KEYS_F10 (17) + +#define RTEMS_COMMAND_BUFFER_SIZE (75) + +static char monitor_prompt[32]; +static char buffer[RTEMS_COMMAND_BUFFER_SIZE]; +static int pos; +static int logged_in; + +/* + * History data. + */ + +#define RTEMS_COMMAND_HISTORIES (20) + +static char history_buffer[RTEMS_COMMAND_HISTORIES][RTEMS_COMMAND_BUFFER_SIZE]; +static int history_pos[RTEMS_COMMAND_HISTORIES]; +static int history; +static int history_next; + +/* + * Translation tables. Not sure if this is the best way to + * handle this, how-ever I wish to avoid the overhead of + * including a more complete and standard environment such + * as ncurses. + */ + +struct translation_table +{ + char expecting; + const struct translation_table *branch; + unsigned int key; +}; + +static const struct translation_table trans_two[] = +{ + { '~', 0, KEYS_INS }, + { 0, 0, 0 } +}; + +static const struct translation_table trans_three[] = +{ + { '~', 0, KEYS_DEL }, + { 0, 0, 0 } +}; + +static const struct translation_table trans_tab_csi[] = +{ + { '2', trans_two, 0 }, + { '3', trans_three, 0 }, + { 'A', 0, KEYS_UARROW }, + { 'B', 0, KEYS_DARROW }, + { 'D', 0, KEYS_LARROW }, + { 'C', 0, KEYS_RARROW }, + { 'F', 0, KEYS_END }, + { 'H', 0, KEYS_HOME }, + { 0, 0, 0 } +}; + +static const struct translation_table trans_tab_O[] = +{ + { '1', 0, KEYS_F1 }, + { '2', 0, KEYS_F2 }, + { '3', 0, KEYS_F3 }, + { '4', 0, KEYS_F4 }, + { '5', 0, KEYS_F5 }, + { '6', 0, KEYS_F6 }, + { '7', 0, KEYS_F7 }, + { '8', 0, KEYS_F8 }, + { '9', 0, KEYS_F9 }, + { ':', 0, KEYS_F10 }, + { 'P', 0, KEYS_F1 }, + { 'Q', 0, KEYS_F2 }, + { 'R', 0, KEYS_F3 }, + { 'S', 0, KEYS_F4 }, + { 'T', 0, KEYS_F5 }, + { 'U', 0, KEYS_F6 }, + { 'V', 0, KEYS_F7 }, + { 'W', 0, KEYS_F8 }, + { 'X', 0, KEYS_F9 }, + { 'Y', 0, KEYS_F10 }, + { 0, 0, 0 } +}; + +static const struct translation_table trans_tab[] = +{ + { '[', trans_tab_csi, 0 }, /* CSI command sequences */ + { 'O', trans_tab_O, 0 }, /* O are the fuction keys */ + { 0, 0, 0 } +}; + +/* + * Perform a basic translation for some ANSI/VT100 key codes. + * This code could do with a timeout on the ESC as it is + * now lost from the input stream. It is not* used by the + * line editor below so considiered not worth the effort. + */ + +static unsigned int +rtems_monitor_getchar (void) +{ + const struct translation_table *translation = 0; + for (;;) + { + char c = getchar (); + if (c == 27) + translation = trans_tab; + else + { + /* + * If no translation happing just pass through + * and return the key. + */ + if (translation) + { + /* + * Scan the current table for the key, and if found + * see if this key is a fork. If so follow it and + * wait else return the extended key. + */ + int index = 0; + int branched = 0; + while ((translation[index].expecting != '\0') || + (translation[index].key != '\0')) + { + if (translation[index].expecting == c) + { + /* + * A branch is take if more keys are to come. + */ + if (translation[index].branch == 0) + return KEYS_EXTENDED | translation[index].key; + else + { + translation = translation[index].branch; + branched = 1; + break; + } + } + index++; + } + /* + * Who knows what these keys are, just drop them. + */ + if (!branched) + translation = 0; + } + else + return c; + } + } +} + +/* + * The line editor with history. + */ + +static int +rtems_monitor_line_editor ( + char *command +) +{ + int repeating = 0; + + memset (buffer, 0, RTEMS_COMMAND_BUFFER_SIZE); + history = history_next; + pos = 0; + + if (!logged_in) + fprintf(stdout,"\nMonitor ready, press enter to login.\n\n"); + else + fprintf(stdout,"%s $ ", monitor_prompt); + + while (1) + { + unsigned int extended_key; + char c; + + fflush (stdout); + + extended_key = rtems_monitor_getchar (); + c = extended_key & KEYS_NORMAL_MASK; + + /* + * Make the extended_key usable as a boolean. + */ + extended_key &= ~KEYS_NORMAL_MASK; + + if (!extended_key && !logged_in) + { + if (c == '\n') + { + logged_in = 1; + /* + * The prompt has changed from `>' to `$' to help know + * which version of the monitor code people are using. + */ + fprintf(stdout,"%s $ ", monitor_prompt); + } + } + else + { + if (extended_key) + { + switch (c) + { + case KEYS_END: + fprintf(stdout,buffer + pos); + pos = (int) strlen (buffer); + break; + + case KEYS_HOME: + fprintf(stdout,"\r%s $ ", monitor_prompt); + pos = 0; + break; + + case KEYS_LARROW: + if (pos > 0) + { + pos--; + putchar ('\b'); + } + break; + + case KEYS_RARROW: + if ((pos < RTEMS_COMMAND_BUFFER_SIZE) && (buffer[pos] != '\0')) + { + putchar (buffer[pos]); + pos++; + } + break; + + case KEYS_UARROW: + /* + * If we are moving up the histories then we need to save the working + * buffer. + */ + if (history) + { + int end; + int bs; + if (history == history_next) + { + memcpy (history_buffer[history_next], buffer, + RTEMS_COMMAND_BUFFER_SIZE); + history_pos[history_next] = pos; + } + history--; + memcpy (buffer, history_buffer[history], + RTEMS_COMMAND_BUFFER_SIZE); + pos = history_pos[history]; + fprintf(stdout,"\r%*c", RTEMS_COMMAND_BUFFER_SIZE, ' '); + fprintf(stdout,"\r%s $ %s", monitor_prompt, buffer); + end = (int) strlen (buffer); + for (bs = 0; bs < (end - pos); bs++) + putchar ('\b'); + } + break; + + case KEYS_DARROW: + if (history < history_next) + { + int end; + int bs; + history++; + memcpy (buffer, history_buffer[history], + RTEMS_COMMAND_BUFFER_SIZE); + pos = history_pos[history]; + fprintf(stdout,"\r%*c", RTEMS_COMMAND_BUFFER_SIZE, ' '); + fprintf(stdout,"\r%s $ %s", monitor_prompt, buffer); + end = (int) strlen (buffer); + for (bs = 0; bs < (end - pos); bs++) + putchar ('\b'); + } + break; + + case KEYS_DEL: + if (buffer[pos] != '\0') + { + int end; + int bs; + strcpy (&buffer[pos], &buffer[pos + 1]); + fprintf(stdout,"\r%s $ %s", monitor_prompt, buffer); + end = (int) strlen (buffer); + for (bs = 0; bs < (end - pos); bs++) + putchar ('\b'); + } + break; + } + } + else + { + switch (c) + { + case '\b': + case '\x7e': + case '\x7f': + if (pos > 0) + { + int bs; + pos--; + strcpy (buffer + pos, buffer + pos + 1); + fprintf(stdout,"\b%s \b", buffer + pos); + for (bs = 0; bs < ((int) strlen (buffer) - pos); bs++) + putchar ('\b'); + } + break; + + case '\n': + /* + * Process the command. + */ + fprintf(stdout,"\n"); + repeating = 1; + /* + * Only process the history if we have a command and + *a history. + */ + if (strlen (buffer)) + { + if (history_next && (history == history_next)) + { + /* + * Do not place the last command into the history + *if the same. + */ + if (strcmp (history_buffer[history_next - 1], buffer)) + repeating = 0; + } + else + repeating = 0; + } + if (!repeating) + { + memcpy (history_buffer[history_next], buffer, + RTEMS_COMMAND_BUFFER_SIZE); + history_pos[history_next] = pos; + if (history_next < (RTEMS_COMMAND_HISTORIES - 1)) + history_next++; + else + { + memmove (history_buffer[0], history_buffer[1], + RTEMS_COMMAND_BUFFER_SIZE * (RTEMS_COMMAND_HISTORIES - 1)); + memmove (&history_pos[0], &history_pos[1], + sizeof (history_pos[0]) * (RTEMS_COMMAND_HISTORIES - 1)); + } + } + else + { +#ifdef ENABLE_ENTER_REPEATS + if (history_next) + memcpy (buffer, history_buffer[history_next - 1], + RTEMS_COMMAND_BUFFER_SIZE); +#endif + } + memmove (command, buffer, RTEMS_COMMAND_BUFFER_SIZE); + return repeating; + break; + + default: + if ((pos < (RTEMS_COMMAND_BUFFER_SIZE - 1)) && + (c >= ' ') && (c <= 'z')) + { + int end; + end = strlen (buffer); + if ((pos < end) && (end < RTEMS_COMMAND_BUFFER_SIZE)) + { + int ch, bs; + for (ch = end; ch > pos; ch--) + buffer[ch] = buffer[ch - 1]; + fprintf(stdout,buffer + pos); + for (bs = 0; bs < (end - pos + 1); bs++) + putchar ('\b'); + } + buffer[pos++] = c; + if (pos > end) + buffer[pos] = '\0'; + putchar (c); + } + break; + } + } + } + } +} + +/* + * make_argv(cp): token-count + * Break up the command line in 'cp' into global argv[] and argc (return + * value). + */ + +int +rtems_monitor_make_argv( + char *cp, + int *argc_p, + char **argv) +{ + int argc = 0; + + while ((cp = strtok(cp, " \t\n\r"))) + { + argv[argc++] = cp; + cp = (char *) NULL; + } + argv[argc] = (char *) NULL; /* end of argv */ + + return *argc_p = argc; +} + + +/* + * Read and break up a monitor command + * + * We have to loop on the gets call, since it will return NULL under UNIX + * RTEMS when we get a signal (eg: SIGALRM). + */ + +int +rtems_monitor_command_read(char *command, + int *argc, + char **argv) +{ + char *env_prompt; + + env_prompt = getenv("RTEMS_MONITOR_PROMPT"); + + /* + * put node number in the prompt if we are multiprocessing + */ +#if defined(RTEMS_MULTIPROCESSING) + if (!rtems_configuration_get_user_multiprocessing_table ()) + sprintf (monitor_prompt, "%s", + (env_prompt == NULL) ? MONITOR_PROMPT: env_prompt); + else /* .... */ +#endif + if (rtems_monitor_default_node != rtems_monitor_node) + sprintf (monitor_prompt, "%" PRId32 "-%s-%" PRId32 "", rtems_monitor_node, + (env_prompt == NULL) ? MONITOR_PROMPT : env_prompt, + rtems_monitor_default_node); + else + sprintf (monitor_prompt, "%" PRId32 "-%s", rtems_monitor_node, + (env_prompt == NULL) ? MONITOR_PROMPT : env_prompt); + + rtems_monitor_line_editor (command); + + return rtems_monitor_make_argv (command, argc, argv); +} + +/* + * Main monitor command loop + */ + +void +rtems_monitor_task( + rtems_task_argument monitor_flags +) +{ + rtems_tcb *debugee = 0; + rtems_context *rp; +#if (CPU_HARDWARE_FP == TRUE) || (CPU_SOFTWARE_FP == TRUE) + rtems_context_fp *fp; +#endif + char command_buffer[513]; + int argc; + char *argv[64]; + bool verbose = false; + struct termios term; + + /* + * Make the stdin stream characte not line based. + */ + + if (tcgetattr (STDIN_FILENO, &term) < 0) + { + fprintf(stdout,"rtems-monitor: cannot get terminal attributes.\n"); + } + else + { + /* + * No echo, no canonical processing. + */ + + term.c_lflag &= ~(ECHO | ICANON | IEXTEN); + + /* + * No sigint on BREAK, CR-to-NL off, input parity off, + * don't strip 8th bit on input, output flow control off + */ + + term.c_lflag &= ~(INPCK | ISTRIP | IXON); + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 0; + + if (tcsetattr (STDIN_FILENO, TCSANOW, &term) < 0) + { + fprintf(stdout,"cannot set terminal attributes\n"); + } + } + + if (!(monitor_flags & RTEMS_MONITOR_NOSYMLOAD)) { + rtems_monitor_symbols_loadup(); + } + + if (monitor_flags & RTEMS_MONITOR_SUSPEND) + (void) rtems_monitor_suspend(RTEMS_NO_TIMEOUT); + + for (;;) + { + const rtems_monitor_command_entry_t *command; + + debugee = _Thread_Executing; + rp = &debugee->Registers; +#if (CPU_HARDWARE_FP == TRUE) || (CPU_SOFTWARE_FP == TRUE) + fp = debugee->fp_context; /* possibly 0 */ +#endif + + if (0 == rtems_monitor_command_read(command_buffer, &argc, argv)) + continue; + if (argc < 1 + || (command = rtems_monitor_command_lookup(argv [0])) == 0) { + /* no command */ + fprintf(stdout,"Unrecognised command; try 'help'\n"); + continue; + } + + command->command_function(argc, argv, &command->command_arg, verbose); + + fflush(stdout); + } +} + + +void +rtems_monitor_kill(void) +{ + if (rtems_monitor_task_id) + rtems_task_delete(rtems_monitor_task_id); + rtems_monitor_task_id = 0; + + rtems_monitor_server_kill(); +} + +void +rtems_monitor_init( + uint32_t monitor_flags +) +{ + rtems_status_code status; + + rtems_monitor_kill(); + + status = rtems_task_create(RTEMS_MONITOR_NAME, + 1, + RTEMS_MINIMUM_STACK_SIZE * 2, + RTEMS_INTERRUPT_LEVEL(0), + RTEMS_DEFAULT_ATTRIBUTES, + &rtems_monitor_task_id); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "could not create monitor task"); + return; + } + + rtems_monitor_node = rtems_object_id_get_node(rtems_monitor_task_id); + rtems_monitor_default_node = rtems_monitor_node; + + rtems_monitor_server_init(monitor_flags); + + if (!(monitor_flags & RTEMS_MONITOR_NOTASK)) { + /* + * Start the monitor task itself + */ + status = rtems_task_start( + rtems_monitor_task_id, rtems_monitor_task, monitor_flags); + if (status != RTEMS_SUCCESSFUL) { + rtems_error(status, "could not start monitor"); + return; + } + } +} diff --git a/cpukit/libmisc/monitor/mon-extension.c b/cpukit/libmisc/monitor/mon-extension.c new file mode 100644 index 0000000000..c6a9f72040 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-extension.c @@ -0,0 +1,102 @@ +/* + * RTEMS Monitor extension support + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/monitor.h> + +#include <stdio.h> + +void +rtems_monitor_extension_canonical( + rtems_monitor_extension_t *canonical_extension, + void *extension_void +) +{ + Extension_Control *rtems_extension = (Extension_Control *) extension_void; + rtems_extensions_table *e = &rtems_extension->Extension.Callouts; + + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_create, + (void *) e->thread_create); + + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_start, + (void *) e->thread_start); + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_restart, + (void *) e->thread_restart); + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_delete, + (void *) e->thread_delete); + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_tswitch, + (void *) e->thread_switch); + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_begin, + (void *) e->thread_begin); + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_exitted, + (void *) e->thread_exitted); + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_fatal, + (void *) e->fatal); +} + +void +rtems_monitor_extension_dump_header( + bool verbose __attribute__((unused)) +) +{ + fprintf(stdout,"\ + ID NAME\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +0 1 2 3 4 5 6 7 */ + + rtems_monitor_separator(); +} + + +/* + * Dump out the canonical form + */ + +void +rtems_monitor_extension_dump( + rtems_monitor_extension_t *monitor_extension, + bool verbose +) +{ + uint32_t length = 0; + + length += rtems_monitor_dump_id(monitor_extension->id); + length += rtems_monitor_pad(11, length); + length += rtems_monitor_dump_name(monitor_extension->id); + + length += rtems_monitor_pad(18, length); + length += fprintf(stdout,"create: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_create, verbose); + length += fprintf(stdout,"; start: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_start, verbose); + length += fprintf(stdout,"; restart: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_restart, verbose); + length += fprintf(stdout,"\n"); + length = 0; + + length += rtems_monitor_pad(18, length); + length += fprintf(stdout,"delete: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_delete, verbose); + length += fprintf(stdout,"; switch: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_tswitch, verbose); + length += fprintf(stdout,"; begin: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_begin, verbose); + length += fprintf(stdout,"\n"); + length = 0; + + length += rtems_monitor_pad(18, length); + length += fprintf(stdout,"exitted: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_exitted, verbose); + length += fprintf(stdout,"; fatal: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_fatal, verbose); + length += fprintf(stdout,"\n"); + length = 0; + fprintf(stdout,"\n"); +} diff --git a/cpukit/libmisc/monitor/mon-itask.c b/cpukit/libmisc/monitor/mon-itask.c new file mode 100644 index 0000000000..6dab51a768 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-itask.c @@ -0,0 +1,121 @@ +/* + * RTEMS Monitor init task support + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#include <rtems.h> +#include <rtems/monitor.h> + +#include <inttypes.h> +#include <stdio.h> + +/* + * As above, but just for init tasks + */ +void +rtems_monitor_init_task_canonical( + rtems_monitor_init_task_t *canonical_itask, + void *itask_void +) +{ + rtems_initialization_tasks_table *rtems_itask = itask_void; + + rtems_monitor_symbol_canonical_by_value(&canonical_itask->entry, + (void *) rtems_itask->entry_point); + + canonical_itask->argument = rtems_itask->argument; + canonical_itask->stack_size = rtems_itask->stack_size; + canonical_itask->priority = rtems_itask->initial_priority; + canonical_itask->modes = rtems_itask->mode_set; + canonical_itask->attributes = rtems_itask->attribute_set; +} + +void * +rtems_monitor_init_task_next( + void *object_info __attribute__((unused)), + rtems_monitor_init_task_t *canonical_init_task, + rtems_id *next_id +) +{ + rtems_initialization_tasks_table *itask; + uint32_t n = rtems_object_id_get_index(*next_id); + + if (n >= Configuration_RTEMS_API.number_of_initialization_tasks) + goto failed; + + _Thread_Disable_dispatch(); + + itask = Configuration_RTEMS_API.User_initialization_tasks_table + n; + + /* + * dummy up a fake id and name for this item + */ + + canonical_init_task->id = n; + canonical_init_task->name = itask->name; + + *next_id += 1; + return (void *) itask; + +failed: + *next_id = RTEMS_OBJECT_ID_FINAL; + return 0; +} + + +void +rtems_monitor_init_task_dump_header( + bool verbose __attribute__((unused)) +) +{ + fprintf(stdout,"\ + # NAME ENTRY ARGUMENT PRIO MODES ATTRIBUTES STACK SIZE\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +0 1 2 3 4 5 6 7 */ + rtems_monitor_separator(); +} + +/* + */ + +void +rtems_monitor_init_task_dump( + rtems_monitor_init_task_t *monitor_itask, + bool verbose +) +{ + int length = 0; + + length += rtems_monitor_dump_decimal(monitor_itask->id); + + length += rtems_monitor_pad(7, length); + length += rtems_monitor_dump_name(monitor_itask->id); + + length += rtems_monitor_pad(14, length); + length += rtems_monitor_symbol_dump(&monitor_itask->entry, verbose); + + length += rtems_monitor_pad(25, length); + length += fprintf(stdout,"%" PRId32 " [0x%" PRIx32 "]", + monitor_itask->argument, monitor_itask->argument); + + length += rtems_monitor_pad(39, length); + length += rtems_monitor_dump_priority(monitor_itask->priority); + + length += rtems_monitor_pad(46, length); + length += rtems_monitor_dump_modes(monitor_itask->modes); + + length += rtems_monitor_pad(54, length); + length += rtems_monitor_dump_attributes(monitor_itask->attributes); + + length += rtems_monitor_pad(66, length); + length += fprintf(stdout,"%" PRId32 " [0x%" PRIx32 "]", + monitor_itask->stack_size, monitor_itask->stack_size); + + fprintf(stdout,"\n"); +} diff --git a/cpukit/libmisc/monitor/mon-manager.c b/cpukit/libmisc/monitor/mon-manager.c new file mode 100644 index 0000000000..9e71addfae --- /dev/null +++ b/cpukit/libmisc/monitor/mon-manager.c @@ -0,0 +1,55 @@ +/* + * RTEMS Monitor "manager" support. + * Used to traverse object (chain) lists and print them out. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/monitor.h> + +#include <stdio.h> + +/* + * "next" routine for all objects that are RTEMS manager objects + */ + +void * +rtems_monitor_manager_next( + void *table_void, + void *canonical, + rtems_id *next_id +) +{ + Objects_Information *table = table_void; + rtems_monitor_generic_t *copy; + Objects_Control *object = 0; + Objects_Locations location; + + /* + * When we are called, it must be local + */ + +#if defined(RTEMS_MULTIPROCESSING) + if ( ! _Objects_Is_local_id(*next_id) ) + goto done; +#endif + + object = _Objects_Get_next(table, *next_id, &location, next_id); + + if (object) + { + copy = (rtems_monitor_generic_t *) canonical; + copy->id = object->id; + copy->name = object->name.name_u32; + } + +#if defined(RTEMS_MULTIPROCESSING) +done: +#endif + return object; +} diff --git a/cpukit/libmisc/monitor/mon-monitor.c b/cpukit/libmisc/monitor/mon-monitor.c new file mode 100644 index 0000000000..88f3d49eb3 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-monitor.c @@ -0,0 +1,529 @@ +/* + * RTEMS monitor main body + * + * TODO: + * add stuff to RTEMS api + * rtems_get_name(id) + * rtems_get_type(id) + * rtems_build_id(node, type, num) + * Add a command to dump out info about an arbitrary id when + * types are added to id's + * rtems> id idnum + * idnum: node n, object: whatever, id: whatever + * allow id's to be specified as n:t:id, where 'n:t' is optional + * should have a separate monitor FILE stream (ala the debugger) + * remote request/response stuff should be cleaned up + * maybe we can use real rpc?? + * 'info' command to print out: + * interrupt stack location, direction and size + * floating point config stuff + * interrupt config stuff + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <rtems/monitor.h> + +/* set by trap handler */ +extern rtems_tcb *debugger_interrupted_task; +extern rtems_context *debugger_interrupted_task_context; +extern uint32_t debugger_trap; + +/* + * Various id's for the monitor + * They need to be public variables for access by other agencies + * such as debugger and remote servers' + */ + +rtems_id rtems_monitor_task_id; + +uint32_t rtems_monitor_node; /* our node number */ +uint32_t rtems_monitor_default_node; /* current default for commands */ + +/* + * The rtems symbol table + */ + +rtems_symbol_table_t *rtems_monitor_symbols; + +/* + * The top-level commands + */ + +static const rtems_monitor_command_entry_t rtems_monitor_commands[] = { + { "config", + "Show the system configuration.", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_CONFIG }, + &rtems_monitor_commands[1], + }, + { "itask", + "List init tasks for the system", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_INIT_TASK }, + &rtems_monitor_commands[2], + }, + { "mpci", + "Show the MPCI system configuration, if configured.", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_MPCI }, + &rtems_monitor_commands[3], + }, + { "pause", + "Monitor goes to \"sleep\" for specified ticks (default is 1). " + "Monitor will resume at end of period or if explicitly awakened\n" + " pause [ticks]", + 0, + rtems_monitor_pause_cmd, + { 0 }, + &rtems_monitor_commands[4], + }, + { "continue", + "Put the monitor to sleep waiting for an explicit wakeup from the " + "program running.\n", + 0, + rtems_monitor_continue_cmd, + { 0 }, + &rtems_monitor_commands[5], + }, + { "go", + "Alias for 'continue'", + 0, + rtems_monitor_continue_cmd, + { 0 }, + &rtems_monitor_commands[6], + }, + { "symbol", + "Display value associated with specified symbol. " + "Defaults to displaying all known symbols.\n" + " symbol [ symbolname [symbolname ... ] ]", + 0, + rtems_monitor_symbol_cmd, + { .symbol_table = &rtems_monitor_symbols }, + &rtems_monitor_commands[7], + }, + { "extension", + "Display information about specified extensions. " + "Default is to display information about all extensions on this node.\n" + " extension [id [id ...] ]", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_EXTENSION }, + &rtems_monitor_commands[8], + }, + { "task", + "Display information about the specified tasks. " + "Default is to display information about all tasks on this node.\n" + " task [id [id ...] ]", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_TASK }, + &rtems_monitor_commands[9], + }, + { "queue", + "Display information about the specified message queues. " + "Default is to display information about all queues on this node.\n" + " queue [id [id ... ] ]", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_QUEUE }, + &rtems_monitor_commands[10], + }, + { "sema", + "sema [id [id ... ] ]\n" + " display information about the specified semaphores\n" + " Default is to display information about all semaphores on this node\n" + , + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_SEMAPHORE }, + &rtems_monitor_commands[11], + }, + { "region", + "region [id [id ... ] ]\n" + " display information about the specified regions\n" + " Default is to display information about all regions on this node\n" + , + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_REGION }, + &rtems_monitor_commands[12], + }, + { "part", + "part [id [id ... ] ]\n" + " display information about the specified partitions\n" + " Default is to display information about all partitions on this node\n" + , + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_PARTITION }, + &rtems_monitor_commands[13], + }, + { "object", + "Display information about specified RTEMS objects. " + "Object id's must include 'type' information. " + "(which may normally be defaulted)\n" + " object [id [id ...] ]", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_INVALID }, + &rtems_monitor_commands[14], + }, + { "driver", + "Display the RTEMS device driver table.\n" + " driver [ major [ major ... ] ]", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_DRIVER }, + &rtems_monitor_commands[15], + }, + { "dname", + "Displays information about named drivers.\n", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_DNAME }, + &rtems_monitor_commands[16], + }, + { "exit", + "Invoke 'rtems_fatal_error_occurred' with 'status' " + "(default is RTEMS_SUCCESSFUL)\n" + " exit [status]", + 0, + rtems_monitor_fatal_cmd, + { .status_code = RTEMS_SUCCESSFUL }, + &rtems_monitor_commands[17], + }, + { "fatal", + "'exit' with fatal error; default error is RTEMS_TASK_EXITTED\n" + " fatal [status]", + 0, + rtems_monitor_fatal_cmd, + { .status_code = RTEMS_TASK_EXITTED }, /* exit value */ + &rtems_monitor_commands[18], + }, + { "quit", + "Alias for 'exit'\n", + 0, + rtems_monitor_fatal_cmd, + { .status_code = RTEMS_SUCCESSFUL }, /* exit value */ + &rtems_monitor_commands[19], + }, + { "reset", + "(SW)Resets the System.", + 0, + rtems_monitor_reset_cmd, + { 0 }, + &rtems_monitor_commands[20], + }, +#if defined(RTEMS_MULTIPROCESSING) + { "node", + "Specify default node number for commands that take id's.\n" + " node [ node number ]", + 0, + rtems_monitor_node_cmd, + { 0 }, + &rtems_monitor_commands[21], + }, + #define RTEMS_MONITOR_POSIX_NEXT 22 +#else + #define RTEMS_MONITOR_POSIX_NEXT 21 +#endif +#ifdef RTEMS_POSIX_API + { "pthread", + "Display information about the specified pthreads. " + "Default is to display information about all pthreads on this node.\n" + " pthread [id [id ...] ]", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_PTHREAD }, + &rtems_monitor_commands[RTEMS_MONITOR_POSIX_NEXT], + }, + #define RTEMS_MONITOR_DEBUGGER_NEXT (RTEMS_MONITOR_POSIX_NEXT + 1) +#else + #define RTEMS_MONITOR_DEBUGGER_NEXT RTEMS_MONITOR_POSIX_NEXT +#endif +#ifdef CPU_INVOKE_DEBUGGER + { "debugger", + "Enter the debugger, if possible. " + "A continue from the debugger will return to the monitor.\n", + 0, + rtems_monitor_debugger_cmd, + { 0 }, + &rtems_monitor_commands[RTEMS_MONITOR_DEBUGGER_NEXT], + }, +#endif + { "help", + "Provide information about commands. " + "Default is show basic command summary.\n" + "help [ command [ command ] ]", + 0, + rtems_monitor_help_cmd, + { .monitor_command_entry = rtems_monitor_commands }, + NULL + } +}; + +/* + * All registered commands. + */ + +static const rtems_monitor_command_entry_t *rtems_monitor_registered_commands = + &rtems_monitor_commands [0]; + + +rtems_status_code +rtems_monitor_suspend(rtems_interval timeout) +{ + rtems_event_set event_set; + rtems_status_code status; + + status = rtems_event_receive(MONITOR_WAKEUP_EVENT, + RTEMS_DEFAULT_OPTIONS, + timeout, + &event_set); + return status; +} + +void __attribute__((weak)) +rtems_monitor_reset_cmd( + int argc, + char **argv, + const rtems_monitor_command_arg_t* command_arg, + bool verbose +) +{ + +} + +void +rtems_monitor_wakeup(void) +{ + rtems_status_code status; + + status = rtems_event_send(rtems_monitor_task_id, MONITOR_WAKEUP_EVENT); +} + +void rtems_monitor_debugger_cmd( + int argc __attribute__((unused)), + char **argv __attribute__((unused)), + const rtems_monitor_command_arg_t *command_arg __attribute__((unused)), + bool verbose __attribute__((unused)) +) +{ +#ifdef CPU_INVOKE_DEBUGGER + CPU_INVOKE_DEBUGGER; +#endif +} + +void rtems_monitor_pause_cmd( + int argc, + char **argv, + const rtems_monitor_command_arg_t *command_arg __attribute__((unused)), + bool verbose __attribute__((unused)) +) +{ + if (argc == 1) + rtems_monitor_suspend(1); + else + rtems_monitor_suspend(strtoul(argv[1], 0, 0)); +} + +void rtems_monitor_fatal_cmd( + int argc, + char **argv, + const rtems_monitor_command_arg_t *command_arg, + bool verbose __attribute__((unused)) +) +{ + if (argc == 1) + rtems_fatal_error_occurred(command_arg->status_code); + else + rtems_fatal_error_occurred(strtoul(argv[1], 0, 0)); +} + +void rtems_monitor_continue_cmd( + int argc __attribute__((unused)), + char **argv __attribute__((unused)), + const rtems_monitor_command_arg_t *command_arg __attribute__((unused)), + bool verbose __attribute__((unused)) +) +{ + rtems_monitor_suspend(RTEMS_NO_TIMEOUT); +} + +#if defined(RTEMS_MULTIPROCESSING) +void rtems_monitor_node_cmd( + int argc, + char **argv, + const rtems_monitor_command_arg_t *command_arg __attribute__((unused)), + bool verbose __attribute__((unused)) +) +{ + uint32_t new_node = rtems_monitor_default_node; + + switch (argc) { + case 1: /* no node, just set back to ours */ + new_node = rtems_monitor_node; + break; + + case 2: + new_node = strtoul(argv[1], 0, 0); + break; + + default: + fprintf(stdout,"invalid syntax, try 'help node'\n"); + break; + } + + if ((new_node >= 1) && + _Configuration_MP_table && + (new_node <= _Configuration_MP_table->maximum_nodes)) + rtems_monitor_default_node = new_node; +} +#endif + + +/* + * Function: rtems_monitor_symbols_loadup + * + * Description: + * Create and load the monitor's symbol table. + * We are reading the output format of 'gnm' which looks like this: + * + * 400a7068 ? _Rate_monotonic_Information + * 400a708c ? _Thread_Dispatch_disable_level + * 400a7090 ? _Configuration_Table + * + * We ignore the type field. + * + * Side Effects: + * Creates and fills in 'rtems_monitor_symbols' table + * + * TODO + * there should be a BSP #define or something like that + * to do this; Assuming stdio is crazy. + * Someday this should know BFD + * Maybe we could get objcopy to just copy the symbol areas + * and copy that down. + * + */ + +void +rtems_monitor_symbols_loadup(void) +{ + FILE *fp; + char buffer[128]; + + if (rtems_monitor_symbols) + rtems_symbol_table_destroy(rtems_monitor_symbols); + + rtems_monitor_symbols = rtems_symbol_table_create(); + if (rtems_monitor_symbols == 0) + return; + + fp = fopen("symbols", "r"); + + if (fp == 0) + return; + + while (fgets(buffer, sizeof(buffer) - 1, fp)) + { + char *symbol; + char *value; + char *ignored_type; + + value = strtok(buffer, " \t\n"); + ignored_type = strtok(0, " \t\n"); + symbol = strtok(0, " \t\n"); + + if (symbol && ignored_type && value) + { + rtems_symbol_t *sp; + sp = rtems_symbol_create(rtems_monitor_symbols, + symbol, + (uint32_t) strtoul(value, 0, 16)); + if (sp == 0) + { + fprintf(stdout,"could not define symbol '%s'\n", symbol); + goto done; + } + } + else + { + fprintf(stdout,"parsing error on '%s'\n", buffer); + goto done; + } + } + +done: + fclose(fp); + return; +} + +/* + * User registered commands. + */ + +int +rtems_monitor_insert_cmd ( + rtems_monitor_command_entry_t *command +) +{ + const rtems_monitor_command_entry_t *e = rtems_monitor_registered_commands; + + /* Reject empty commands */ + if (command->command == NULL) { + return 0; + } + + /* Reject command if already present */ + while (e->next != NULL) { + if (e->command != NULL && strcmp(command->command, e->command) == 0) { + return 0; + } + e = e->next; + } + + /* Prepend new command */ + command->next = rtems_monitor_registered_commands; + rtems_monitor_registered_commands = command; + + return 1; +} + +/** + * @brief Iterates through all registerd commands. + * + * For each command the interation routine @a routine is called with the + * command entry and the user provided argument @a arg. It is guaranteed that + * the command name and function are not NULL. + */ +void rtems_monitor_command_iterate( + rtems_monitor_per_command_routine routine, + void *arg +) +{ + const rtems_monitor_command_entry_t *e = rtems_monitor_registered_commands; + + while (e != NULL) { + if (e->command != NULL && e->command_function != NULL) { + if (!routine(e, arg)) { + break; + } + } + e = e->next; + } +} diff --git a/cpukit/libmisc/monitor/mon-mpci.c b/cpukit/libmisc/monitor/mon-mpci.c new file mode 100644 index 0000000000..ab77431241 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-mpci.c @@ -0,0 +1,163 @@ +/* + * RTEMS MPCI Config display support + * + * TODO + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#include <rtems.h> +#include <rtems/monitor.h> + +#include <stdio.h> +#include <stdlib.h> /* strtoul() */ +#include <inttypes.h> + +#define DATACOL 15 + +/* + * Fill in entire monitor config table + * for sending to a remote monitor or printing on the local system + */ + +void +rtems_monitor_mpci_canonical( + rtems_monitor_mpci_t *canonical_mpci, + void *config_void +) +{ + rtems_configuration_table *c = &Configuration; + rtems_multiprocessing_table *m; + rtems_mpci_table *mt; + + m = c->User_multiprocessing_table; + if (m == 0) + return; + mt = m->User_mpci_table; + + canonical_mpci->node = m->node; + canonical_mpci->maximum_nodes = m->maximum_nodes; + canonical_mpci->maximum_global_objects = m->maximum_global_objects; + canonical_mpci->maximum_proxies = m->maximum_proxies; + + canonical_mpci->default_timeout = mt->default_timeout; + canonical_mpci->maximum_packet_size = mt->maximum_packet_size; + + rtems_monitor_symbol_canonical_by_value(&canonical_mpci->initialization, + (void *) mt->initialization); + + rtems_monitor_symbol_canonical_by_value(&canonical_mpci->get_packet, + (void *) mt->get_packet); + rtems_monitor_symbol_canonical_by_value(&canonical_mpci->return_packet, + (void *) mt->return_packet); + rtems_monitor_symbol_canonical_by_value(&canonical_mpci->send_packet, + (void *) mt->send_packet); + rtems_monitor_symbol_canonical_by_value(&canonical_mpci->receive_packet, + (void *) mt->receive_packet); +} + +/* + * This is easy, since there is only 1 (altho we could get them from + * other nodes...) + */ + +void * +rtems_monitor_mpci_next( + void *object_info, + rtems_monitor_mpci_t *canonical_mpci, + rtems_id *next_id +) +{ + rtems_configuration_table *c = &Configuration; + int n = rtems_object_id_get_index(*next_id); + + if (n >= 1) + goto failed; + + if ( ! c->User_multiprocessing_table) + goto failed; + + _Thread_Disable_dispatch(); + + *next_id += 1; + return (void *) c; + +failed: + *next_id = RTEMS_OBJECT_ID_FINAL; + return 0; +} + + +void +rtems_monitor_mpci_dump_header( + bool verbose +) +{ + fprintf(stdout,"\ + max max max default max\n\ + node nodes globals proxies timeout pktsize\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +0 1 2 3 4 5 6 7 */ + + rtems_monitor_separator(); +} + + +void +rtems_monitor_mpci_dump( + rtems_monitor_mpci_t *monitor_mpci, + bool verbose +) +{ + uint32_t length = 0; + + length += rtems_monitor_pad(2, length); + length += fprintf(stdout," %" PRId32 , monitor_mpci->node); + length += rtems_monitor_pad(11, length); + length += fprintf(stdout,"%" PRId32, monitor_mpci->maximum_nodes); + + length += rtems_monitor_pad(18, length); + length += rtems_monitor_dump_decimal(monitor_mpci->maximum_global_objects); + + length += rtems_monitor_pad(28, length); + length += rtems_monitor_dump_decimal(monitor_mpci->maximum_proxies); + + length += rtems_monitor_pad(37, length); + length += rtems_monitor_dump_decimal(monitor_mpci->default_timeout); + + length += rtems_monitor_pad(46, length); + length += rtems_monitor_dump_decimal((uint32_t) monitor_mpci->maximum_packet_size); + + fprintf(stdout,"\n"); + length = 0; + length += rtems_monitor_pad(DATACOL, length); + + length += fprintf(stdout,"init: "); + length += rtems_monitor_symbol_dump(&monitor_mpci->initialization, verbose); + + fprintf(stdout,"\n"); + length = 0; + length += rtems_monitor_pad(DATACOL, length); + + length += fprintf(stdout,"get: "); + length += rtems_monitor_symbol_dump(&monitor_mpci->get_packet, verbose); + length += fprintf(stdout,"; return: "); + length += rtems_monitor_symbol_dump(&monitor_mpci->return_packet, verbose); + + fprintf(stdout,"\n"); + length = 0; + length += rtems_monitor_pad(DATACOL, length); + + length += fprintf(stdout,"send: "); + length += rtems_monitor_symbol_dump(&monitor_mpci->send_packet, verbose); + length += fprintf(stdout,"; receive: "); + length += rtems_monitor_symbol_dump(&monitor_mpci->receive_packet, verbose); + + fprintf(stdout,"\n"); + length = 0; +} diff --git a/cpukit/libmisc/monitor/mon-network.c b/cpukit/libmisc/monitor/mon-network.c new file mode 100644 index 0000000000..52c0cd9852 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-network.c @@ -0,0 +1,342 @@ +/* + * COPYRIGHT (c) 1989-2007. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> + +#include <rtems/rtems_bsdnet.h> +#include <sys/socket.h> +#include <net/if.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/sockio.h> +#include <net/route.h> + + +void mon_ifconfig(int argc, char *argv[], + uint32_t command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + struct sockaddr_in ipaddr; + struct sockaddr_in dstaddr; + struct sockaddr_in netmask; + struct sockaddr_in broadcast; + char *iface; + int f_ip = 0; + int f_ptp = 0; + int f_netmask = 0; + int f_up = 0; + int f_down = 0; + int f_bcast = 0; + int cur_idx; + int rc; + int flags; + + memset(&ipaddr, 0, sizeof(ipaddr)); + memset(&dstaddr, 0, sizeof(dstaddr)); + memset(&netmask, 0, sizeof(netmask)); + memset(&broadcast, 0, sizeof(broadcast)); + + ipaddr.sin_len = sizeof(ipaddr); + ipaddr.sin_family = AF_INET; + + dstaddr.sin_len = sizeof(dstaddr); + dstaddr.sin_family = AF_INET; + + netmask.sin_len = sizeof(netmask); + netmask.sin_family = AF_INET; + + broadcast.sin_len = sizeof(broadcast); + broadcast.sin_family = AF_INET; + + cur_idx = 0; + if (argc <= 1) { + /* display all interfaces */ + iface = NULL; + cur_idx += 1; + } else { + iface = argv[1]; + if (isdigit((unsigned char)*argv[2])) { + if (inet_pton(AF_INET, argv[2], &ipaddr.sin_addr) < 0) { + printf("bad ip address: %s\n", argv[2]); + return; + } + f_ip = 1; + cur_idx += 3; + } else { + cur_idx += 2; + } + } + + if ((f_down !=0) && (f_ip != 0)) { + f_up = 1; + } + + while(argc > cur_idx) { + if (strcmp(argv[cur_idx], "up") == 0) { + f_up = 1; + if (f_down != 0) { + printf("Can't make interface up and down\n"); + } + } else if(strcmp(argv[cur_idx], "down") == 0) { + f_down = 1; + if (f_up != 0) { + printf("Can't make interface up and down\n"); + } + } else if(strcmp(argv[cur_idx], "netmask") == 0) { + if ((cur_idx + 1) >= argc) { + printf("No netmask address\n"); + return; + } + if (inet_pton(AF_INET, argv[cur_idx+1], &netmask.sin_addr) < 0) { + printf("bad netmask: %s\n", argv[cur_idx]); + return; + } + f_netmask = 1; + cur_idx += 1; + } else if(strcmp(argv[cur_idx], "broadcast") == 0) { + if ((cur_idx + 1) >= argc) { + printf("No broadcast address\n"); + return; + } + if (inet_pton(AF_INET, argv[cur_idx+1], &broadcast.sin_addr) < 0) { + printf("bad broadcast: %s\n", argv[cur_idx]); + return; + } + f_bcast = 1; + cur_idx += 1; + } else if(strcmp(argv[cur_idx], "pointopoint") == 0) { + if ((cur_idx + 1) >= argc) { + printf("No pointopoint address\n"); + return; + } + if (inet_pton(AF_INET, argv[cur_idx+1], &dstaddr.sin_addr) < 0) { + printf("bad pointopoint: %s\n", argv[cur_idx]); + return; + } + + f_ptp = 1; + cur_idx += 1; + } else { + printf("Bad parameter: %s\n", argv[cur_idx]); + return; + } + + cur_idx += 1; + } + + printf("ifconfig "); + if (iface != NULL) { + printf("%s ", iface); + if (f_ip != 0) { + char str[256]; + inet_ntop(AF_INET, &ipaddr.sin_addr, str, 256); + printf("%s ", str); + } + + if (f_netmask != 0) { + char str[256]; + inet_ntop(AF_INET, &netmask.sin_addr, str, 256); + printf("netmask %s ", str); + } + + if (f_bcast != 0) { + char str[256]; + inet_ntop(AF_INET, &broadcast.sin_addr, str, 256); + printf("broadcast %s ", str); + } + + if (f_ptp != 0) { + char str[256]; + inet_ntop(AF_INET, &dstaddr.sin_addr, str, 256); + printf("pointopoint %s ", str); + } + + if (f_up != 0) { + printf("up\n"); + } else if (f_down != 0) { + printf("down\n"); + } else { + printf("\n"); + } + } + + if ((iface == NULL) || ((f_ip == 0) && (f_down == 0) && (f_up == 0))) { + rtems_bsdnet_show_if_stats(); + return; + } + + flags = 0; + if (f_netmask) { + rc = rtems_bsdnet_ifconfig(iface, SIOCSIFNETMASK, &netmask); + if (rc < 0) { + printf("Could not set netmask: %s\n", strerror(errno)); + return; + } + } + + if (f_bcast) { + rc = rtems_bsdnet_ifconfig(iface, SIOCSIFBRDADDR, &broadcast); + if (rc < 0) { + printf("Could not set broadcast: %s\n", strerror(errno)); + return; + } + } + + if (f_ptp) { + rc = rtems_bsdnet_ifconfig(iface, SIOCSIFDSTADDR, &dstaddr); + if (rc < 0) { + printf("Could not set destination address: %s\n", strerror(errno)); + return; + } + flags |= IFF_POINTOPOINT; + } + + /* This must come _after_ setting the netmask, broadcast addresses */ + if (f_ip) { + rc = rtems_bsdnet_ifconfig(iface, SIOCSIFADDR, &ipaddr); + if (rc < 0) { + printf("Could not set IP address: %s\n", strerror(errno)); + return; + } + } + + if (f_up != 0) { + flags |= IFF_UP; + } + + if (f_down != 0) { + printf("Warning: taking interfaces down is not supported\n"); + } + + rc = rtems_bsdnet_ifconfig(iface, SIOCSIFFLAGS, &flags); + if (rc < 0) { + printf("Could not set interface flags: %s\n", strerror(errno)); + return; + } +} + + + +void mon_route(int argc, char *argv[], + uint32_t command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + int cmd; + struct sockaddr_in dst; + struct sockaddr_in gw; + struct sockaddr_in netmask; + int f_host; + int f_gw = 0; + int cur_idx; + int flags; + int rc; + + memset(&dst, 0, sizeof(dst)); + memset(&gw, 0, sizeof(gw)); + memset(&netmask, 0, sizeof(netmask)); + + dst.sin_len = sizeof(dst); + dst.sin_family = AF_INET; + dst.sin_addr.s_addr = inet_addr("0.0.0.0"); + + gw.sin_len = sizeof(gw); + gw.sin_family = AF_INET; + gw.sin_addr.s_addr = inet_addr("0.0.0.0"); + + netmask.sin_len = sizeof(netmask); + netmask.sin_family = AF_INET; + netmask.sin_addr.s_addr = inet_addr("255.255.255.0"); + + if (argc < 2) { + rtems_bsdnet_show_inet_routes(); + return; + } + + if (strcmp(argv[1], "add") == 0) { + cmd = RTM_ADD; + } else if (strcmp(argv[1], "del") == 0) { + cmd = RTM_DELETE; + } else { + printf("invalid command: %s\n", argv[1]); + printf("\tit should be 'add' or 'del'\n"); + return; + } + + if (argc < 3) { + printf("not enough arguments\n"); + return; + } + + if (strcmp(argv[2], "-host") == 0) { + f_host = 1; + } else if (strcmp(argv[2], "-net") == 0) { + f_host = 0; + } else { + printf("Invalid type: %s\n", argv[1]); + printf("\tit should be '-host' or '-net'\n"); + return; + } + + if (argc < 4) { + printf("not enough arguments\n"); + return; + } + + inet_pton(AF_INET, argv[3], &dst.sin_addr); + + cur_idx = 4; + while(cur_idx < argc) { + if (strcmp(argv[cur_idx], "gw") == 0) { + if ((cur_idx +1) >= argc) { + printf("no gateway address\n"); + return; + } + f_gw = 1; + inet_pton(AF_INET, argv[cur_idx + 1], &gw.sin_addr); + cur_idx += 1; + } else if(strcmp(argv[cur_idx], "netmask") == 0) { + if ((cur_idx +1) >= argc) { + printf("no netmask address\n"); + return; + } + f_gw = 1; + inet_pton(AF_INET, argv[cur_idx + 1], &netmask.sin_addr); + cur_idx += 1; + } else { + printf("Unknown argument: %s\n", argv[cur_idx]); + return; + } + cur_idx += 1; + } + + flags = RTF_STATIC; + if (f_gw != 0) { + flags |= RTF_GATEWAY; + } + if (f_host != 0) { + flags |= RTF_HOST; + } + + rc = rtems_bsdnet_rtrequest(cmd, + (struct sockaddr*) &dst, + (struct sockaddr*) &gw, + (struct sockaddr*) &netmask, flags, NULL); + if (rc < 0) { + printf("Error adding route\n"); + } +} diff --git a/cpukit/libmisc/monitor/mon-object.c b/cpukit/libmisc/monitor/mon-object.c new file mode 100644 index 0000000000..96b2a85f8d --- /dev/null +++ b/cpukit/libmisc/monitor/mon-object.c @@ -0,0 +1,423 @@ +/* + * RTEMS Monitor "object" support. + * + * Used to traverse object lists and print them out. + * An object can be an RTEMS object (chain based stuff) or + * a "misc" object such as a device driver. + * + * Each object has its own file in this directory (eg: extension.c) + * That file provides routines to convert a "native" structure + * to its canonical form, print a canonical structure, etc. + * + * TODO: + * should allow for non-numeric id's??? + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#include <rtems.h> +#include <rtems/monitor.h> +#if defined(RTEMS_POSIX_API) + #include <rtems/posix/pthread.h> +#endif + +#include <stdio.h> +#include <stdlib.h> /* strtoul() */ +#include <string.h> /* memcpy() */ + +#define NUMELEMS(arr) (sizeof(arr) / sizeof(arr[0])) + +/* + * add: + * next + */ + +static const rtems_monitor_object_info_t rtems_monitor_object_info[] = +{ + { RTEMS_MONITOR_OBJECT_CONFIG, + (void *) 0, + sizeof(rtems_monitor_config_t), + (rtems_monitor_object_next_fn) rtems_monitor_config_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_config_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_config_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_config_dump, + }, + { RTEMS_MONITOR_OBJECT_MPCI, + (void *) 0, +#if defined(RTEMS_MULTIPROCESSING) + sizeof(rtems_monitor_mpci_t), + (rtems_monitor_object_next_fn) rtems_monitor_mpci_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_mpci_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_mpci_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_mpci_dump, +#else + 0, + (rtems_monitor_object_next_fn) 0, + (rtems_monitor_object_canonical_fn) 0, + (rtems_monitor_object_dump_header_fn) 0, + (rtems_monitor_object_dump_fn) 0, +#endif + }, + { RTEMS_MONITOR_OBJECT_INIT_TASK, + (void *) 0, + sizeof(rtems_monitor_init_task_t), + (rtems_monitor_object_next_fn) rtems_monitor_init_task_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_init_task_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_init_task_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_init_task_dump, + }, + { RTEMS_MONITOR_OBJECT_TASK, + (void *) &_RTEMS_tasks_Information, + sizeof(rtems_monitor_task_t), + (rtems_monitor_object_next_fn) rtems_monitor_manager_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_task_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_task_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_task_dump, + }, + { RTEMS_MONITOR_OBJECT_QUEUE, + (void *) &_Message_queue_Information, + sizeof(rtems_monitor_queue_t), + (rtems_monitor_object_next_fn) rtems_monitor_manager_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_queue_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_queue_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_queue_dump, + }, + { RTEMS_MONITOR_OBJECT_SEMAPHORE, + (void *) &_Semaphore_Information, + sizeof(rtems_monitor_sema_t), + (rtems_monitor_object_next_fn) rtems_monitor_manager_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_sema_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_sema_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_sema_dump, + }, + { RTEMS_MONITOR_OBJECT_REGION, + (void *) &_Region_Information, + sizeof(rtems_monitor_region_t), + (rtems_monitor_object_next_fn) rtems_monitor_manager_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_region_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_region_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_region_dump, + }, + { RTEMS_MONITOR_OBJECT_PARTITION, + (void *) &_Partition_Information, + sizeof(rtems_monitor_part_t), + (rtems_monitor_object_next_fn) rtems_monitor_manager_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_part_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_part_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_part_dump, + }, + { RTEMS_MONITOR_OBJECT_EXTENSION, + (void *) &_Extension_Information, + sizeof(rtems_monitor_extension_t), + (rtems_monitor_object_next_fn) rtems_monitor_manager_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_extension_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_extension_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_extension_dump, + }, + { RTEMS_MONITOR_OBJECT_DRIVER, + (void *) 0, + sizeof(rtems_monitor_driver_t), + (rtems_monitor_object_next_fn) rtems_monitor_driver_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_driver_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_driver_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_driver_dump, + }, +#if defined(RTEMS_POSIX_API) + { RTEMS_MONITOR_OBJECT_PTHREAD, + (void *) &_POSIX_Threads_Information, + sizeof(rtems_monitor_task_t), + (rtems_monitor_object_next_fn) rtems_monitor_manager_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_task_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_task_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_task_dump, + }, +#endif +}; + +/* + * Allow id's to be specified without the node number or + * type for convenience. + */ + +rtems_id +rtems_monitor_id_fixup( + rtems_id id, + uint32_t default_node, + rtems_monitor_object_type_t type +) +{ + uint32_t node; + + node = rtems_object_id_get_node(id); + if (node == 0) + { + if (rtems_object_id_get_class(id) != OBJECTS_CLASSIC_NO_CLASS) + type = rtems_object_id_get_class(id); + + id = rtems_build_id( + OBJECTS_CLASSIC_API, + type, + default_node, + rtems_object_id_get_index(id) + ); + } + return id; +} + + +const rtems_monitor_object_info_t * +rtems_monitor_object_lookup( + rtems_monitor_object_type_t type +) +{ + const rtems_monitor_object_info_t *p; + for (p = &rtems_monitor_object_info[0]; + p < &rtems_monitor_object_info[NUMELEMS(rtems_monitor_object_info)]; + p++) + { + if (p->type == type) + return p; + } + return 0; +} + +rtems_id +rtems_monitor_object_canonical_next_remote( + rtems_monitor_object_type_t type, + rtems_id id, + void *canonical +) +{ + rtems_id next_id; + rtems_status_code status; + rtems_monitor_server_request_t request; + rtems_monitor_server_response_t response; + + /* + * Send request + */ + + request.command = RTEMS_MONITOR_SERVER_CANONICAL; + request.argument0 = (uint32_t) type; + request.argument1 = (uint32_t) id; + + status = rtems_monitor_server_request( + rtems_object_id_get_node(id), &request, &response); + if (status != RTEMS_SUCCESSFUL) + goto failed; + + /* + * process response + */ + + next_id = (rtems_id) response.result0; + if (next_id != RTEMS_OBJECT_ID_FINAL) + (void) memcpy(canonical, &response.payload, response.result1); + + return next_id; + +failed: + return RTEMS_OBJECT_ID_FINAL; + +} + + +rtems_id +rtems_monitor_object_canonical_next( + const rtems_monitor_object_info_t *info, + rtems_id id, + void *canonical +) +{ + rtems_id next_id; + void *raw_item; + +#if defined(RTEMS_MULTIPROCESSING) + if ( ! _Objects_Is_local_id(id) ) { + next_id = rtems_monitor_object_canonical_next_remote( + info->type, + id, + canonical + ); + } else +#endif + { + next_id = id; + + raw_item = (void *) info->next( + info->object_information, + canonical, + &next_id + ); + + if (raw_item) { + info->canonical(canonical, raw_item); + _Thread_Enable_dispatch(); + } + } + return next_id; +} + + +/* + * this is routine server invokes locally to get the type + */ + +rtems_id +rtems_monitor_object_canonical_get( + rtems_monitor_object_type_t type, + rtems_id id, + void *canonical, + size_t *size_p +) +{ + const rtems_monitor_object_info_t *info; + rtems_id next_id; + + *size_p = 0; + + info = rtems_monitor_object_lookup(type); + + if (info == 0) + return RTEMS_OBJECT_ID_FINAL; + + next_id = rtems_monitor_object_canonical_next(info, id, canonical); + *size_p = info->size; + + return next_id; +} + + +void +rtems_monitor_object_dump_1( + const rtems_monitor_object_info_t *info, + rtems_id id, + bool verbose +) +{ + rtems_id next_id; + rtems_monitor_union_t canonical; + + if ((next_id = rtems_monitor_object_canonical_next( + info, + id, + &canonical)) != RTEMS_OBJECT_ID_FINAL) + { + /* + * If the one we actually got is the one we wanted, then + * print it out. + * For ones that have an id field, this works fine, + * for all others, always dump it out. + * + * HACK: the way we determine whether there is an id is a hack. + * + * by the way: the reason we try to not have an id, is that some + * of the canonical structures are almost too big for shared + * memory driver (eg: mpci) + */ + + if ((info->next != rtems_monitor_manager_next) || + (id == canonical.generic.id)) + info->dump(&canonical, verbose); + } +} + +void +rtems_monitor_object_dump_all( + const rtems_monitor_object_info_t *info, + bool verbose +) +{ + rtems_id next_id; + rtems_monitor_union_t canonical; + + next_id = RTEMS_OBJECT_ID_INITIAL(OBJECTS_CLASSIC_API, info->type, rtems_monitor_default_node); + + while ((next_id = rtems_monitor_object_canonical_next( + info, + next_id, + &canonical)) != RTEMS_OBJECT_ID_FINAL) + { + info->dump(&canonical, verbose); + } +} + +void +rtems_monitor_object_cmd( + int argc, + char **argv, + const rtems_monitor_command_arg_t *command_arg, + bool verbose +) +{ + int arg; + const rtems_monitor_object_info_t *info = 0; + rtems_monitor_object_type_t type; + + /* what is the default type? */ + type = command_arg->monitor_object; + + if (argc == 1) + { + if (type == RTEMS_MONITOR_OBJECT_INVALID) + { + fprintf(stdout,"A type must be specified to \"dump all\"\n"); + goto done; + } + + info = rtems_monitor_object_lookup(type); + if (info == 0) + goto not_found; + + if (info->dump_header) + info->dump_header(verbose); + rtems_monitor_object_dump_all(info, verbose); + } + else + { + uint32_t default_node = rtems_monitor_default_node; + rtems_monitor_object_type_t last_type = RTEMS_MONITOR_OBJECT_INVALID; + rtems_id id; + + for (arg=1; argv[arg]; arg++) + { + id = (rtems_id) strtoul(argv[arg], 0, 16); + id = rtems_monitor_id_fixup(id, default_node, type); + type = (rtems_monitor_object_type_t) rtems_object_id_get_class(id); + + /* + * Allow the item type to change in the middle + * of the command. If the type changes, then + * just dump out a new header and keep on going. + */ + if (type != last_type) + { + info = rtems_monitor_object_lookup(type); + if (info == 0) + goto not_found; + + if (info->dump_header) + info->dump_header(verbose); + } + + if (info == 0) + { +not_found: fprintf(stdout,"Invalid or unsupported type %d\n", type); + goto done; + } + + rtems_monitor_object_dump_1(info, id, verbose); + + default_node = rtems_object_id_get_node(id); + + last_type = type; + } + } +done: + return; +} diff --git a/cpukit/libmisc/monitor/mon-part.c b/cpukit/libmisc/monitor/mon-part.c new file mode 100644 index 0000000000..72aff6921f --- /dev/null +++ b/cpukit/libmisc/monitor/mon-part.c @@ -0,0 +1,72 @@ +/* + * RTEMS Monitor partition support + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include "monitor.h" +#include <rtems/rtems/attr.inl> +#include <stdio.h> +#include <string.h> /* memcpy() */ + +void +rtems_monitor_part_canonical( + rtems_monitor_part_t *canonical_part, + void *part_void +) +{ + Partition_Control *rtems_part = (Partition_Control *) part_void; + + canonical_part->attribute = rtems_part->attribute_set; + canonical_part->start_addr = rtems_part->starting_address; + canonical_part->length = rtems_part->length; + canonical_part->buf_size = rtems_part->buffer_size; + canonical_part->used_blocks = rtems_part->number_of_used_blocks; +} + + +void +rtems_monitor_part_dump_header( + bool verbose __attribute__((unused)) +) +{ + printf("\ + ID NAME ATTR STARTADDR LENGTH BUF_SIZE USED_BLOCKS\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 + 1 2 3 4 5 6 7 */ + + rtems_monitor_separator(); +} + +/* + */ + +void +rtems_monitor_part_dump( + rtems_monitor_part_t *monitor_part, + bool verbose __attribute__((unused)) +) +{ + int length = 0; + + length += rtems_monitor_dump_id(monitor_part->id); + length += rtems_monitor_pad(11, length); + length += rtems_monitor_dump_name(monitor_part->id); + length += rtems_monitor_pad(18, length); + length += rtems_monitor_dump_attributes(monitor_part->attribute); + length += rtems_monitor_pad(30, length); + length += rtems_monitor_dump_addr(monitor_part->start_addr); + length += rtems_monitor_pad(40, length); + length += rtems_monitor_dump_hex(monitor_part->length); + length += rtems_monitor_pad(50, length); + length += rtems_monitor_dump_hex(monitor_part->buf_size); + length += rtems_monitor_pad(60, length); + length += rtems_monitor_dump_hex(monitor_part->used_blocks); + printf("\n"); +} + diff --git a/cpukit/libmisc/monitor/mon-prmisc.c b/cpukit/libmisc/monitor/mon-prmisc.c new file mode 100644 index 0000000000..32ee6711b4 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-prmisc.c @@ -0,0 +1,259 @@ +/* + * Print misc stuff for the monitor dump routines + * Each routine returns the number of characters it output. + * + * TODO: + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/monitor.h> + +#include <rtems/assoc.h> + +#include <stdio.h> +#include <ctype.h> +#include <inttypes.h> + +void +rtems_monitor_separator(void) +{ + fprintf(stdout,"------------------------------------------------------------------------------\n"); +} + +uint32_t +rtems_monitor_pad( + uint32_t destination_column, + uint32_t current_column +) +{ + int pad_length; + + if (destination_column <= current_column) + pad_length = 1; + else + pad_length = destination_column - current_column; + + return fprintf(stdout,"%*s", pad_length, ""); +} + +int +rtems_monitor_dump_decimal(uint32_t num) +{ + return fprintf(stdout,"%4" PRId32, num); +} + +int +rtems_monitor_dump_addr(void *addr) +{ + return fprintf(stdout,"0x%p", addr); +} + +int +rtems_monitor_dump_hex(uint32_t num) +{ + return fprintf(stdout,"0x%" PRIx32, num); +} + +int +rtems_monitor_dump_assoc_bitfield( + const rtems_assoc_t *ap, + const char *separator, + uint32_t value + ) +{ + uint32_t b; + uint32_t length = 0; + const char *name; + + for (b = 1; b; b <<= 1) + if (b & value) + { + if (length) + length += fprintf(stdout,"%s", separator); + + name = rtems_assoc_name_by_local(ap, b); + + if (name) + length += fprintf(stdout,"%s", name); + else + length += fprintf(stdout,"0x%" PRIx32, b); + } + + return length; +} + +int +rtems_monitor_dump_id(rtems_id id) +{ +#if defined(RTEMS_USE_16_BIT_OBJECT) + return fprintf(stdout,"%08" PRIx16, id); +#else + return fprintf(stdout,"%08" PRIx32, id); +#endif +} + +int +rtems_monitor_dump_name(rtems_id id) +{ + char name_buffer[18] = "????"; + + rtems_object_get_name( id, sizeof(name_buffer), name_buffer ); + + return fprintf( stdout, name_buffer ); +} + +int +rtems_monitor_dump_priority(rtems_task_priority priority) +{ + return fprintf(stdout,"%3" PRId32, priority); +} + + +static const rtems_assoc_t rtems_monitor_state_assoc[] = { + { "DORM", STATES_DORMANT, 0 }, + { "SUSP", STATES_SUSPENDED, 0 }, + { "TRANS", STATES_TRANSIENT, 0 }, + { "DELAY", STATES_DELAYING, 0 }, + { "Wtime", STATES_WAITING_FOR_TIME, 0 }, + { "Wbuf", STATES_WAITING_FOR_BUFFER, 0 }, + { "Wseg", STATES_WAITING_FOR_SEGMENT, 0 }, + { "Wmsg" , STATES_WAITING_FOR_MESSAGE, 0 }, + { "Wevnt", STATES_WAITING_FOR_EVENT, 0 }, + { "Wsem", STATES_WAITING_FOR_SEMAPHORE, 0 }, + { "Wmutex", STATES_WAITING_FOR_MUTEX, 0 }, + { "Wcvar", STATES_WAITING_FOR_CONDITION_VARIABLE, 0 }, + { "Wjatx", STATES_WAITING_FOR_JOIN_AT_EXIT, 0 }, + { "Wrpc", STATES_WAITING_FOR_RPC_REPLY, 0 }, + { "WRATE", STATES_WAITING_FOR_PERIOD, 0 }, + { "Wsig", STATES_WAITING_FOR_SIGNAL, 0 }, + { "Wbar", STATES_WAITING_FOR_BARRIER, 0 }, + { "Wrwlk", STATES_WAITING_FOR_RWLOCK, 0 }, + { "Wisig", STATES_INTERRUPTIBLE_BY_SIGNAL, 0 }, + { 0, 0, 0 }, +}; + +int +rtems_monitor_dump_state(States_Control state) +{ + int length = 0; + + if (state == STATES_READY) /* assoc doesn't deal with this as it is 0 */ + length += fprintf(stdout,"READY"); + + length += rtems_monitor_dump_assoc_bitfield(rtems_monitor_state_assoc, + ":", + state); + return length; +} + +static const rtems_assoc_t rtems_monitor_attribute_assoc[] = { + { "GL", RTEMS_GLOBAL, 0 }, + { "PR", RTEMS_PRIORITY, 0 }, + { "FL", RTEMS_FLOATING_POINT, 0 }, + { "BI", RTEMS_BINARY_SEMAPHORE, 0 }, + { "SB", RTEMS_SIMPLE_BINARY_SEMAPHORE, 0 }, + { "IN", RTEMS_INHERIT_PRIORITY, 0 }, + { "CE", RTEMS_PRIORITY_CEILING, 0 }, + { "AR", RTEMS_BARRIER_AUTOMATIC_RELEASE, 0 }, + { "ST", RTEMS_SYSTEM_TASK, 0 }, + { 0, 0, 0 }, +}; + +int +rtems_monitor_dump_attributes(rtems_attribute attributes) +{ + int length = 0; + + if (attributes == RTEMS_DEFAULT_ATTRIBUTES) /* value is 0 */ + length += fprintf(stdout,"DEFAULT"); + + length += rtems_monitor_dump_assoc_bitfield(rtems_monitor_attribute_assoc, + ":", + attributes); + return length; +} + +static const rtems_assoc_t rtems_monitor_modes_assoc[] = { + { "nP", RTEMS_NO_PREEMPT, 0 }, + { "T", RTEMS_TIMESLICE, 0 }, + { "nA", RTEMS_NO_ASR, 0 }, + { 0, 0, 0 }, +}; + +int +rtems_monitor_dump_modes(rtems_mode modes) +{ + uint32_t length = 0; + + if (modes == RTEMS_DEFAULT_MODES) /* value is 0 */ + length += fprintf(stdout,"P:T:nA"); + + length += rtems_monitor_dump_assoc_bitfield(rtems_monitor_modes_assoc, + ":", + modes); + return length; +} + +static const rtems_assoc_t rtems_monitor_events_assoc[] = { + { "0", RTEMS_EVENT_0, 0 }, + { "1", RTEMS_EVENT_1, 0 }, + { "2", RTEMS_EVENT_2, 0 }, + { "3", RTEMS_EVENT_3, 0 }, + { "4", RTEMS_EVENT_4, 0 }, + { "5", RTEMS_EVENT_5, 0 }, + { "6", RTEMS_EVENT_6, 0 }, + { "7", RTEMS_EVENT_7, 0 }, + { "8", RTEMS_EVENT_8, 0 }, + { "9", RTEMS_EVENT_9, 0 }, + { "10", RTEMS_EVENT_10, 0 }, + { "11", RTEMS_EVENT_11, 0 }, + { "12", RTEMS_EVENT_12, 0 }, + { "13", RTEMS_EVENT_13, 0 }, + { "14", RTEMS_EVENT_14, 0 }, + { "15", RTEMS_EVENT_15, 0 }, + { "16", RTEMS_EVENT_16, 0 }, + { "17", RTEMS_EVENT_17, 0 }, + { "18", RTEMS_EVENT_18, 0 }, + { "19", RTEMS_EVENT_19, 0 }, + { "20", RTEMS_EVENT_20, 0 }, + { "21", RTEMS_EVENT_21, 0 }, + { "22", RTEMS_EVENT_22, 0 }, + { "23", RTEMS_EVENT_23, 0 }, + { "24", RTEMS_EVENT_24, 0 }, + { "25", RTEMS_EVENT_25, 0 }, + { "26", RTEMS_EVENT_26, 0 }, + { "27", RTEMS_EVENT_27, 0 }, + { "28", RTEMS_EVENT_28, 0 }, + { "29", RTEMS_EVENT_29, 0 }, + { "30", RTEMS_EVENT_30, 0 }, + { "31", RTEMS_EVENT_31, 0 }, + { 0, 0, 0 }, +}; + +int +rtems_monitor_dump_events(rtems_event_set events) +{ + if (events == EVENT_SETS_NONE_PENDING) /* value is 0 */ + return fprintf(stdout," NONE "); + + return fprintf(stdout,"%08" PRIx32, events); +} + +int +rtems_monitor_dump_notepad(uint32_t *notepad) +{ + int length = 0; + int i; + + for (i=0; i < RTEMS_NUMBER_NOTEPADS; i++) + if (notepad[i]) + length += fprintf(stdout,"%d: 0x%" PRIx32, i, notepad[i]); + + return length; +} diff --git a/cpukit/libmisc/monitor/mon-queue.c b/cpukit/libmisc/monitor/mon-queue.c new file mode 100644 index 0000000000..4237b01780 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-queue.c @@ -0,0 +1,68 @@ +/* + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/monitor.h> + +#include <stdio.h> + +void +rtems_monitor_queue_canonical( + rtems_monitor_queue_t *canonical_queue, + void *queue_void +) +{ + Message_queue_Control *rtems_queue = (Message_queue_Control *) queue_void; + + canonical_queue->attributes = rtems_queue->attribute_set; + canonical_queue->maximum_message_size = rtems_queue->message_queue.maximum_message_size; + canonical_queue->maximum_pending_messages = rtems_queue->message_queue.maximum_pending_messages; + canonical_queue->number_of_pending_messages = rtems_queue->message_queue.number_of_pending_messages; +} + +void +rtems_monitor_queue_dump_header( + bool verbose __attribute__((unused)) +) +{ + fprintf(stdout,"\ + ID NAME ATTRIBUTES PEND MAXPEND MAXSIZE\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +0 1 2 3 4 5 6 7 */ + rtems_monitor_separator(); +} + + +/* + * Dump out the "next" queue indicated by 'id'. + * Returns next one to check. + * Returns RTEMS_OBJECT_ID_FINAL when all done + */ + +void +rtems_monitor_queue_dump( + rtems_monitor_queue_t *monitor_queue, + bool verbose __attribute__((unused)) +) +{ + uint32_t length = 0; + + length += rtems_monitor_dump_id(monitor_queue->id); + length += rtems_monitor_pad(11, length); + length += rtems_monitor_dump_name(monitor_queue->id); + length += rtems_monitor_pad(19, length); + length += rtems_monitor_dump_attributes(monitor_queue->attributes); + length += rtems_monitor_pad(31, length); + length += rtems_monitor_dump_decimal(monitor_queue->number_of_pending_messages); + length += rtems_monitor_pad(39, length); + length += rtems_monitor_dump_decimal(monitor_queue->maximum_pending_messages); + length += rtems_monitor_pad(48, length); + length += rtems_monitor_dump_decimal(monitor_queue->maximum_message_size); + + fprintf(stdout,"\n"); +} diff --git a/cpukit/libmisc/monitor/mon-region.c b/cpukit/libmisc/monitor/mon-region.c new file mode 100644 index 0000000000..271cbf82f4 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-region.c @@ -0,0 +1,73 @@ +/* + * RTEMS Monitor region support + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include "monitor.h" +#include <rtems/rtems/attr.inl> +#include <stdio.h> +#include <string.h> /* memcpy() */ + +void +rtems_monitor_region_canonical( + rtems_monitor_region_t *canonical_region, + void *region_void +) +{ + Region_Control *rtems_region = (Region_Control *) region_void; + + canonical_region->attribute = rtems_region->attribute_set; + canonical_region->start_addr = rtems_region->starting_address; + canonical_region->length = rtems_region->length; + canonical_region->page_size = rtems_region->page_size; + canonical_region->max_seg_size = rtems_region->maximum_segment_size; + canonical_region->used_blocks = rtems_region->number_of_used_blocks; +} + + +void +rtems_monitor_region_dump_header( + bool verbose __attribute__((unused)) +) +{ + printf("\ + ID NAME ATTR STARTADDR LENGTH PAGE_SIZE USED_BLOCKS\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 + 1 2 3 4 5 6 7 */ + + rtems_monitor_separator(); +} + +/* + */ + +void +rtems_monitor_region_dump( + rtems_monitor_region_t *monitor_region, + bool verbose __attribute__((unused)) +) +{ + int length = 0; + + length += rtems_monitor_dump_id(monitor_region->id); + length += rtems_monitor_pad(11, length); + length += rtems_monitor_dump_name(monitor_region->id); + length += rtems_monitor_pad(18, length); + length += rtems_monitor_dump_attributes(monitor_region->attribute); + length += rtems_monitor_pad(30, length); + length += rtems_monitor_dump_addr(monitor_region->start_addr); + length += rtems_monitor_pad(40, length); + length += rtems_monitor_dump_hex(monitor_region->length); + length += rtems_monitor_pad(50, length); + length += rtems_monitor_dump_hex(monitor_region->page_size); + length += rtems_monitor_pad(60, length); + length += rtems_monitor_dump_hex(monitor_region->used_blocks); + printf("\n"); +} + diff --git a/cpukit/libmisc/monitor/mon-sema.c b/cpukit/libmisc/monitor/mon-sema.c new file mode 100644 index 0000000000..276601c04c --- /dev/null +++ b/cpukit/libmisc/monitor/mon-sema.c @@ -0,0 +1,84 @@ +/* + * RTEMS Monitor semaphore support + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include "monitor.h" +#include <rtems/rtems/attr.inl> +#include <stdio.h> +#include <string.h> /* memcpy() */ + +void +rtems_monitor_sema_canonical( + rtems_monitor_sema_t *canonical_sema, + void *sema_void +) +{ + Semaphore_Control *rtems_sema = (Semaphore_Control *) sema_void; + + canonical_sema->attribute = rtems_sema->attribute_set; + canonical_sema->priority_ceiling = + rtems_sema->Core_control.mutex.Attributes.priority_ceiling; + + canonical_sema->holder_id = + rtems_sema->Core_control.mutex.holder_id; + + if (_Attributes_Is_counting_semaphore(canonical_sema->attribute)) { + /* we have a counting semaphore */ + canonical_sema->cur_count = + rtems_sema->Core_control.semaphore.count; + + canonical_sema->max_count = + rtems_sema->Core_control.semaphore.Attributes.maximum_count; + } + else { + /* we have a binary semaphore (mutex) */ + canonical_sema->cur_count = rtems_sema->Core_control.mutex.lock; + canonical_sema->max_count = 1; /* mutex is either 0 or 1 */ + } +} + + +void +rtems_monitor_sema_dump_header( + bool verbose __attribute__((unused)) +) +{ + printf("\ + ID NAME ATTR PRICEIL CURR_CNT HOLDID \n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 + 1 2 3 4 5 6 7 */ + + rtems_monitor_separator(); +} + +/* + */ + +void +rtems_monitor_sema_dump( + rtems_monitor_sema_t *monitor_sema, + bool verbose __attribute__((unused)) +) +{ + int length = 0; + + length += rtems_monitor_dump_id(monitor_sema->id); + length += rtems_monitor_pad(11, length); + length += rtems_monitor_dump_name(monitor_sema->id); + length += rtems_monitor_pad(18, length); + length += rtems_monitor_dump_attributes(monitor_sema->attribute); + length += rtems_monitor_pad(30, length); + length += rtems_monitor_dump_priority(monitor_sema->priority_ceiling); + length += rtems_monitor_pad(38, length); + length += rtems_monitor_dump_decimal(monitor_sema->cur_count); + length += rtems_monitor_pad(47, length); + length += rtems_monitor_dump_id(monitor_sema->holder_id); + printf("\n"); +} diff --git a/cpukit/libmisc/monitor/mon-server.c b/cpukit/libmisc/monitor/mon-server.c new file mode 100644 index 0000000000..03bc1932b2 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-server.c @@ -0,0 +1,309 @@ +/* + * RTEMS monitor server (handles requests for info from RTEMS monitors + * running on other nodes) + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include <rtems/monitor.h> + +/* + * Various id's for the server + */ + +rtems_id rtems_monitor_server_task_id; +rtems_id rtems_monitor_server_request_queue_id; /* our server */ +rtems_id *rtems_monitor_server_request_queue_ids; /* all servers */ +rtems_id rtems_monitor_server_response_queue_id; /* our server */ + + +/* + * Send a request to a server task + */ + +rtems_status_code +rtems_monitor_server_request( + uint32_t server_node, + rtems_monitor_server_request_t *request, + rtems_monitor_server_response_t *response +) +{ + rtems_id server_id; + rtems_status_code status; + size_t size; + + /* + * What is id of monitor on target node? + * Look it up if we don't know it yet. + */ + + server_id = rtems_monitor_server_request_queue_ids[server_node]; + if (server_id == 0) + { + status = rtems_message_queue_ident(RTEMS_MONITOR_QUEUE_NAME, + server_node, + &server_id); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "ident of remote server failed"); + goto done; + } + + rtems_monitor_server_request_queue_ids[server_node] = server_id; + } + + request->return_id = rtems_monitor_server_response_queue_id; + + status = rtems_message_queue_send(server_id, request, sizeof(*request)); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "monitor server request send failed"); + goto done; + } + + /* + * Await response, if requested + */ + + if (response) + { + status = rtems_message_queue_receive(rtems_monitor_server_response_queue_id, + response, + &size, + RTEMS_WAIT, + 100); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "server did not respond"); + + /* maybe server task was restarted; look it up again next time */ + rtems_monitor_server_request_queue_ids[server_node] = 0; + + goto done; + } + + if (response->command != RTEMS_MONITOR_SERVER_RESPONSE) + { + status = RTEMS_INCORRECT_STATE; + goto done; + } + } + +done: + return status; +} + + + +/* + * monitor server task + */ + +void +rtems_monitor_server_task( + rtems_task_argument monitor_flags __attribute__((unused)) +) +{ + rtems_monitor_server_request_t request; + rtems_monitor_server_response_t response; + rtems_status_code status; + size_t size; + + for (;;) + { + status = rtems_message_queue_receive( + rtems_monitor_server_request_queue_id, + &request, + &size, + RTEMS_WAIT, + (rtems_interval) 0); + + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "monitor server msg queue receive error"); + goto failed; + } + + if (size != sizeof(request)) + { + rtems_error(0, "monitor server bad size on receive"); + goto failed; + } + + switch (request.command) + { + case RTEMS_MONITOR_SERVER_CANONICAL: + { + rtems_monitor_object_type_t object_type; + rtems_id id; + rtems_id next_id; + + object_type = (rtems_monitor_object_type_t) request.argument0; + id = (rtems_id) request.argument1; + next_id = rtems_monitor_object_canonical_get(object_type, + id, + &response.payload, + &size); + + response.command = RTEMS_MONITOR_SERVER_RESPONSE; + response.result0 = next_id; + response.result1 = size; + +#define SERVER_OVERHEAD (RTEMS_offsetof(rtems_monitor_server_response_t, \ + payload)) + + status = rtems_message_queue_send(request.return_id, + &response, + size + SERVER_OVERHEAD); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "response send failed"); + goto failed; + } + break; + } + + default: + { + rtems_error(0, "invalid command to monitor server: %d", request.command); + goto failed; + } + } + } + +failed: + rtems_task_delete(RTEMS_SELF); +} + + +/* + * Kill off any old server + * Not sure if this is useful, but it doesn't help + */ + +void +rtems_monitor_server_kill(void) +{ + if (rtems_monitor_server_task_id) + rtems_task_delete(rtems_monitor_server_task_id); + rtems_monitor_server_task_id = 0; + + if (rtems_monitor_server_request_queue_id) + rtems_message_queue_delete(rtems_monitor_server_request_queue_id); + rtems_monitor_server_request_queue_ids = 0; + + if (rtems_monitor_server_response_queue_id) + rtems_message_queue_delete(rtems_monitor_server_response_queue_id); + rtems_monitor_server_response_queue_id = 0; + + if (rtems_monitor_server_request_queue_ids) + free(rtems_monitor_server_request_queue_ids); + rtems_monitor_server_request_queue_ids = 0; +} + + +void +rtems_monitor_server_init( + uint32_t monitor_flags __attribute__((unused)) +) +{ + #if defined(RTEMS_MULTIPROCESSING) + rtems_status_code status; + + if (_System_state_Is_multiprocessing && + (_Configuration_MP_table->maximum_nodes > 1)) + { + uint32_t maximum_nodes = _Configuration_MP_table->maximum_nodes; + + /* + * create the msg que our server will listen + * Since we only get msgs from other RTEMS monitors, we just + * need reserve space for 1 msg from each node. + */ + + status = rtems_message_queue_create( + RTEMS_MONITOR_QUEUE_NAME, + maximum_nodes, + sizeof(rtems_monitor_server_request_t), + RTEMS_GLOBAL, + &rtems_monitor_server_request_queue_id); + + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "could not create monitor server message queue"); + goto done; + } + + /* + * create the msg que our responses will come on + * Since monitor just does one thing at a time, we only need 1 item + * message queue. + */ + + status = rtems_message_queue_create( + RTEMS_MONITOR_RESPONSE_QUEUE_NAME, + 1, /* depth */ + sizeof(rtems_monitor_server_response_t), + RTEMS_GLOBAL, + &rtems_monitor_server_response_queue_id); + + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "could not create monitor response message queue"); + goto done; + } + + /* need an id for queue of each other server we might talk to */ + /* indexed by node, so add 1 to maximum_nodes */ + rtems_monitor_server_request_queue_ids = + (rtems_id *) malloc((maximum_nodes + 1) * sizeof(rtems_id)); + (void) memset(rtems_monitor_server_request_queue_ids, + 0, + (maximum_nodes + 1) * sizeof(rtems_id)); + + rtems_monitor_server_request_queue_ids[rtems_monitor_node] = + rtems_monitor_server_request_queue_id; + + /* + * create the server task + */ + status = rtems_task_create(RTEMS_MONITOR_SERVER_NAME, + 1, + 0 /* default stack */, + RTEMS_INTERRUPT_LEVEL(0), + RTEMS_DEFAULT_ATTRIBUTES, + &rtems_monitor_server_task_id); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "could not create monitor server task"); + goto done; + } + + /* + * Start the server task + */ + status = rtems_task_start(rtems_monitor_server_task_id, + rtems_monitor_server_task, + monitor_flags); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "could not start monitor server"); + goto done; + } + } + +done: + #endif + return; +} diff --git a/cpukit/libmisc/monitor/mon-symbols.c b/cpukit/libmisc/monitor/mon-symbols.c new file mode 100644 index 0000000000..38a5716c6f --- /dev/null +++ b/cpukit/libmisc/monitor/mon-symbols.c @@ -0,0 +1,490 @@ +/* + * File: symbols.c + * + * Description: + * Symbol table manager for the RTEMS monitor. + * These routines may be used by other system resources also. + * + * + * TODO: + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#include <rtems.h> +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> + +#include <rtems/monitor.h> +#include "symbols.h" + + +rtems_symbol_table_t * +rtems_symbol_table_create(void) +{ + rtems_symbol_table_t *table; + + table = (rtems_symbol_table_t *) malloc(sizeof(rtems_symbol_table_t)); + memset((void *) table, 0, sizeof(*table)); + + table->growth_factor = 30; /* 30 percent */ + + return table; +} + +void +rtems_symbol_table_destroy(rtems_symbol_table_t *table) +{ + rtems_symbol_string_block_t *p, *pnext; + + if (table) + { + if (table->addresses) + (void) free(table->addresses); + table->addresses = 0; + p = table->string_buffer_head; + while (p) + { + pnext = p->next; + free(p); + p = pnext; + } + table->string_buffer_head = 0; + table->string_buffer_current = 0; + + free(table); + } +} + +rtems_symbol_t * +rtems_symbol_create( + rtems_symbol_table_t *table, + const char *name, + uint32_t value + ) +{ + size_t symbol_length; + size_t newsize; + rtems_symbol_t *sp; + + symbol_length = strlen(name) + 1; /* include '\000' in length */ + + /* need to grow the table? */ + if (table->next >= table->size) + { + if (table->size == 0) + newsize = 100; + else + newsize = table->size + (table->size / (100 / table->growth_factor)); + + table->addresses = (rtems_symbol_t *) realloc((void *) table->addresses, newsize * sizeof(rtems_symbol_t)); + if (table->addresses == 0) /* blew it; lost orig */ + goto failed; + table->size = newsize; + } + + sp = &table->addresses[table->next]; + sp->value = value; + + /* Have to add it to string pool */ + /* need to grow pool? */ + + if ((table->string_buffer_head == 0) || + (table->strings_next + symbol_length) >= SYMBOL_STRING_BLOCK_SIZE) + { + rtems_symbol_string_block_t *p; + + p = (rtems_symbol_string_block_t *) malloc(sizeof(rtems_symbol_string_block_t)); + if (p == 0) + goto failed; + p->next = 0; + if (table->string_buffer_head == 0) + table->string_buffer_head = p; + else + table->string_buffer_current->next = p; + table->string_buffer_current = p; + + table->strings_next = 0; + } + + sp->name = table->string_buffer_current->buffer + table->strings_next; + (void) strcpy(sp->name, name); + + table->strings_next += symbol_length; + table->sorted = 0; + table->next++; + + return sp; + +/* XXX Not sure what to do here. We've possibly destroyed the initial + symbol table due to realloc failure */ +failed: + return 0; +} + +/* + * Qsort entry point for compare by address + */ + +static int +rtems_symbol_compare(const void *e1, + const void *e2) +{ + rtems_symbol_t *s1, *s2; + s1 = (rtems_symbol_t *) e1; + s2 = (rtems_symbol_t *) e2; + + if (s1->value < s2->value) + return -1; + if (s1->value > s2->value) + return 1; + return 0; +} + + +/* + * Sort the symbol table using qsort + */ + +static void +rtems_symbol_sort(rtems_symbol_table_t *table) +{ + qsort((void *) table->addresses, (size_t) table->next, + sizeof(rtems_symbol_t), rtems_symbol_compare); + table->sorted = 1; +} + + +/* + * Search the symbol table by address + * This code based on CYGNUS newlib bsearch, but changed + * to allow for finding closest symbol <= key + */ + +rtems_symbol_t * +rtems_symbol_value_lookup( + rtems_symbol_table_t *table, + uint32_t value + ) +{ + rtems_symbol_t *sp; + rtems_symbol_t *base; + rtems_symbol_t *best = 0; + uint32_t distance; + uint32_t best_distance = ~0; + uint32_t elements; + + if (table == 0) + table = rtems_monitor_symbols; + + if ((table == 0) || (table->size == 0)) + return 0; + + if (table->sorted == 0) + rtems_symbol_sort(table); + + base = table->addresses; + elements = table->next; + + while (elements) + { + sp = base + (elements / 2); + if (value < sp->value) + elements /= 2; + else if (value > sp->value) + { + distance = value - sp->value; + if (distance < best_distance) + { + best_distance = distance; + best = sp; + } + base = sp + 1; + elements = (elements / 2) - (elements % 2 ? 0 : 1); + } + else + return sp; + } + + if (value == base->value) + return base; + + return best; +} + +/* + * Search the symbol table for the exact matching address. + * If the symbol table has already been sorted, then + * call the regular symbol value lookup, however, it it + * has not yet been sorted, search it sequentially. + * This routine is primarily used for low level symbol + * lookups (eg. from exception handler and interrupt routines) + * where the penality of sorted is not wanted and where + * an exact match is needed such that symbol table order + * is not important. + */ +const rtems_symbol_t * +rtems_symbol_value_lookup_exact( + rtems_symbol_table_t *table, + uint32_t value + ) +{ + uint32_t s; + rtems_symbol_t *sp; + + if (table == 0) + { + table = rtems_monitor_symbols; + if (table == 0) + return NULL; + } + + if (table->sorted) + { + sp = rtems_symbol_value_lookup(table, value); + if ( rtems_symbol_value(sp) == value ) + return sp; + else + return NULL; /* not an exact match */ + } + + for (s = 0, sp = table->addresses; s < table->next; s++, sp++) + { + if ( sp->value == value ) + return sp; + } + + return NULL; + +} + + +/* + * Search the symbol table by string name (case independent) + */ + +rtems_symbol_t * +rtems_symbol_name_lookup( + rtems_symbol_table_t *table, + const char *name + ) +{ + uint32_t s; + rtems_symbol_t *sp; + + if (table == 0) + { + table = rtems_monitor_symbols; + if (table == 0) + return NULL; + } + + for (s = 0, sp = table->addresses; s < table->next; s++, sp++) + { + if ( strcasecmp(sp->name, name) == 0 ) + return sp; + } + + return NULL; +} + +void * +rtems_monitor_symbol_next( + void *object_info, + rtems_monitor_symbol_t *canonical __attribute__((unused)), + rtems_id *next_id +) +{ + rtems_symbol_table_t *table; + uint32_t n = rtems_object_id_get_index(*next_id); + + table = *(rtems_symbol_table_t **) object_info; + if (table == 0) + goto failed; + + if (n >= table->next) + goto failed; + + /* NOTE: symbols do not have id and name fields */ + + if (table->sorted == 0) + rtems_symbol_sort(table); + + _Thread_Disable_dispatch(); + + *next_id += 1; + return (void *) (table->addresses + n); + +failed: + *next_id = RTEMS_OBJECT_ID_FINAL; + return 0; +} + +void +rtems_monitor_symbol_canonical( + rtems_monitor_symbol_t *canonical_symbol, + rtems_symbol_t *sp +) +{ + canonical_symbol->value = sp->value; + canonical_symbol->offset = 0; + strncpy(canonical_symbol->name, sp->name, sizeof(canonical_symbol->name)-1); +} + + +void +rtems_monitor_symbol_canonical_by_name( + rtems_monitor_symbol_t *canonical_symbol, + const char *name +) +{ + rtems_symbol_t *sp; + + sp = rtems_symbol_name_lookup(0, name); + + canonical_symbol->value = sp ? sp->value : 0; + + strncpy(canonical_symbol->name, name, sizeof(canonical_symbol->name) - 1); + canonical_symbol->offset = 0; +} + +void +rtems_monitor_symbol_canonical_by_value( + rtems_monitor_symbol_t *canonical_symbol, + void *value_void_p +) +{ + uintptr_t value = (uintptr_t) value_void_p; + rtems_symbol_t *sp; + + sp = rtems_symbol_value_lookup(0, value); + if (sp) + { + canonical_symbol->value = sp->value; + canonical_symbol->offset = value - sp->value; + strncpy(canonical_symbol->name, sp->name, sizeof(canonical_symbol->name)-1); + } + else + { + canonical_symbol->value = value; + canonical_symbol->offset = 0; + canonical_symbol->name[0] = '\0'; + } +} + + +uint32_t +rtems_monitor_symbol_dump( + rtems_monitor_symbol_t *canonical_symbol, + bool verbose +) +{ + uint32_t length = 0; + + /* + * print the name if it exists AND if value is non-zero + * Ie: don't print some garbage symbol for address 0 + */ + + if (canonical_symbol->name[0] && (canonical_symbol->value != 0)) + { + if (canonical_symbol->offset == 0) + length += fprintf(stdout,"%.*s", + (int) sizeof(canonical_symbol->name), + canonical_symbol->name); + else + length += fprintf(stdout,"<%.*s+0x%" PRIx32 ">", + (int) sizeof(canonical_symbol->name), + canonical_symbol->name, + canonical_symbol->offset); + if (verbose) + length += fprintf(stdout, + " [0x%" PRIx32 "]", canonical_symbol->value); + } + else + length += fprintf(stdout,"[0x%" PRIx32 "]", canonical_symbol->value); + + return length; +} + + +void +rtems_monitor_symbol_dump_all( + rtems_symbol_table_t *table, + bool verbose __attribute__((unused)) +) +{ + uint32_t s; + rtems_symbol_t *sp; + + if (table == 0) + { + table = rtems_monitor_symbols; + if (table == 0) + return; + } + + if (table->sorted == 0) + rtems_symbol_sort(table); + + for (s = 0, sp = table->addresses; s < table->next; s++, sp++) + { + rtems_monitor_symbol_t canonical_symbol; + + rtems_monitor_symbol_canonical(&canonical_symbol, sp); + rtems_monitor_symbol_dump(&canonical_symbol, true); + fprintf(stdout,"\n"); + } +} + + +/* + * 'symbol' command + */ + +void rtems_monitor_symbol_cmd( + int argc, + char **argv, + const rtems_monitor_command_arg_t *command_arg, + bool verbose +) +{ + int arg; + rtems_symbol_table_t *table; + + table = *command_arg->symbol_table; + if (table == 0) + { + table = rtems_monitor_symbols; + if (table == 0) + return; + } + + /* + * Use object command to dump out whole symbol table + */ + if (argc == 1) + rtems_monitor_symbol_dump_all(table, verbose); + else + { + rtems_monitor_symbol_t canonical_symbol; + + for (arg=1; argv[arg]; arg++) + { + rtems_monitor_symbol_canonical_by_name(&canonical_symbol, argv[arg]); + rtems_monitor_symbol_dump(&canonical_symbol, verbose); + fprintf(stdout,"\n"); + } + } +} diff --git a/cpukit/libmisc/monitor/mon-task.c b/cpukit/libmisc/monitor/mon-task.c new file mode 100644 index 0000000000..6007b3f879 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-task.c @@ -0,0 +1,105 @@ +/* + * RTEMS Monitor task support + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/monitor.h> + +#include <stdio.h> +#include <string.h> /* memcpy() */ + +void +rtems_monitor_task_canonical( + rtems_monitor_task_t *canonical_task, + void *thread_void +) +{ + Thread_Control *rtems_thread = (Thread_Control *) thread_void; + RTEMS_API_Control *api; + + api = rtems_thread->API_Extensions[ THREAD_API_RTEMS ]; + + canonical_task->entry = rtems_thread->Start.entry_point; + canonical_task->argument = rtems_thread->Start.numeric_argument; + canonical_task->stack = rtems_thread->Start.Initial_stack.area; + canonical_task->stack_size = rtems_thread->Start.Initial_stack.size; + canonical_task->priority = rtems_thread->current_priority; + canonical_task->state = rtems_thread->current_state; + canonical_task->wait_id = rtems_thread->Wait.id; + canonical_task->events = api->pending_events; + /* + * FIXME: make this optionally cpu_time_executed + */ +#if 0 + canonical_task->ticks = rtems_thread->cpu_time_executed; +#else + canonical_task->ticks = 0; +#endif + +/* XXX modes and attributes only exist in the RTEMS API .. */ +/* XXX not directly in the core thread.. they will have to be derived */ +/* XXX if they are important enough to include anymore. */ + canonical_task->modes = 0; /* XXX FIX ME.... rtems_thread->current_modes; */ + canonical_task->attributes = 0 /* XXX FIX ME rtems_thread->API_Extensions[ THREAD_API_RTEMS ]->attribute_set */; + (void) memcpy(canonical_task->notepad, api ->Notepads, sizeof(canonical_task->notepad)); +/* XXX more to fix */ +/* + (void) memcpy(&canonical_task->wait_args, &rtems_thread->Wait.Extra, sizeof(canonical_task->wait_args)); +*/ +} + + +void +rtems_monitor_task_dump_header( + bool verbose __attribute__((unused)) +) +{ + fprintf(stdout,"\ + ID NAME PRI STATE MODES EVENTS WAITID WAITARG NOTES\n\ +"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +0 1 2 3 4 5 6 7 */ + + rtems_monitor_separator(); +} + +/* + */ + +void +rtems_monitor_task_dump( + rtems_monitor_task_t *monitor_task, + bool verbose __attribute__((unused)) +) +{ + int length = 0; + + length += rtems_monitor_dump_id(monitor_task->id); + length += rtems_monitor_pad(11, length); + length += rtems_monitor_dump_name(monitor_task->id); + length += rtems_monitor_pad(26, length); + length += rtems_monitor_dump_priority(monitor_task->priority); + length += rtems_monitor_pad(29, length); + length += rtems_monitor_dump_state(monitor_task->state); + length += rtems_monitor_pad(37, length); + length += rtems_monitor_dump_modes(monitor_task->modes); + length += rtems_monitor_pad(45, length); + length += rtems_monitor_dump_events(monitor_task->events); + if (monitor_task->wait_id) + { + length += rtems_monitor_pad(54, length); + length += rtems_monitor_dump_id(monitor_task->wait_id); + length += rtems_monitor_pad(63, length); + length += rtems_monitor_dump_hex(monitor_task->wait_args); + } + + length += rtems_monitor_pad(72, length); + length += rtems_monitor_dump_notepad(monitor_task->notepad); + fprintf(stdout,"\n"); +} diff --git a/cpukit/libmisc/monitor/monitor.h b/cpukit/libmisc/monitor/monitor.h new file mode 100644 index 0000000000..84a360b976 --- /dev/null +++ b/cpukit/libmisc/monitor/monitor.h @@ -0,0 +1,531 @@ +/** + * @file rtems/monitor.h + * + * The RTEMS monitor task. + */ + +/* + * $Id$ + */ + +#ifndef __MONITOR_H +#define __MONITOR_H + +#include <rtems/error.h> /* rtems_error() */ +#include <rtems/config.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward decls from symbols.h */ +typedef struct _rtems_symbol_t rtems_symbol_t; +typedef struct _rtems_symbol_table_t rtems_symbol_table_t; + +/* + * Monitor types are derived from rtems object classes + */ + +typedef enum { + RTEMS_MONITOR_OBJECT_INVALID = OBJECTS_CLASSIC_NO_CLASS, + RTEMS_MONITOR_OBJECT_TASK = OBJECTS_RTEMS_TASKS, + RTEMS_MONITOR_OBJECT_EXTENSION = OBJECTS_RTEMS_EXTENSIONS, + RTEMS_MONITOR_OBJECT_QUEUE = OBJECTS_RTEMS_MESSAGE_QUEUES, + RTEMS_MONITOR_OBJECT_SEMAPHORE = OBJECTS_RTEMS_SEMAPHORES, + RTEMS_MONITOR_OBJECT_PARTITION = OBJECTS_RTEMS_PARTITIONS, + RTEMS_MONITOR_OBJECT_REGION = OBJECTS_RTEMS_REGIONS, + RTEMS_MONITOR_OBJECT_PORT = OBJECTS_RTEMS_PORTS, + + /* following monitor objects are not known to RTEMS, but + * we like to have "types" for them anyway */ + + RTEMS_MONITOR_OBJECT_DRIVER = OBJECTS_RTEMS_CLASSES_LAST+1, + RTEMS_MONITOR_OBJECT_DNAME, + RTEMS_MONITOR_OBJECT_CONFIG, + RTEMS_MONITOR_OBJECT_INIT_TASK, + RTEMS_MONITOR_OBJECT_MPCI, + RTEMS_MONITOR_OBJECT_SYMBOL, + #if defined(RTEMS_POSIX_API) + RTEMS_MONITOR_OBJECT_PTHREAD, + #endif +} rtems_monitor_object_type_t; + +/* + * rtems_monitor_init() flags + */ + +#define RTEMS_MONITOR_SUSPEND 0x0001 /* suspend monitor on startup */ +#define RTEMS_MONITOR_GLOBAL 0x0002 /* monitor should be global */ +#define RTEMS_MONITOR_NOTASK 0x0004 /* do not start monitor task */ +#define RTEMS_MONITOR_NOSYMLOAD 0x0008 /* do not load symbols */ +#define RTEMS_MONITOR_WAITQUIT 0x0010 /* wait for monitor task to terminate */ + +/* + * Public interfaces for RTEMS data structures monitor is aware of. + * These are only used by the monitor. + * + * NOTE: + * All the canonical objects that correspond to RTEMS managed "objects" + * must have an identical first portion with 'id' and 'name' fields. + * + * Others do not have that restriction, even tho we would like them to. + * This is because some of the canonical structures are almost too big + * for shared memory driver (eg: mpci) and we are nickel and diming it. + */ + +/* + * Type of a pointer that may be a symbol + */ + +#define MONITOR_SYMBOL_LEN 20 +typedef struct { + char name[MONITOR_SYMBOL_LEN]; + uint32_t value; + uint32_t offset; +} rtems_monitor_symbol_t; + +typedef struct { + rtems_id id; + rtems_name name; + /* end of common portion */ +} rtems_monitor_generic_t; + +/* + * Task + */ +typedef struct { + rtems_id id; + rtems_name name; + /* end of common portion */ + Thread_Entry entry; + Thread_Entry_numeric_type argument; + void *stack; + uint32_t stack_size; + rtems_task_priority priority; + States_Control state; + rtems_event_set events; + rtems_mode modes; + rtems_attribute attributes; + uint32_t notepad[RTEMS_NUMBER_NOTEPADS]; + rtems_id wait_id; + uint32_t wait_args; + uint32_t ticks; +} rtems_monitor_task_t; + +/* + * Init task + */ + +typedef struct { + rtems_id id; /* not really an id */ + rtems_name name; + /* end of common portion */ + rtems_monitor_symbol_t entry; + uint32_t argument; + uint32_t stack_size; + rtems_task_priority priority; + rtems_mode modes; + rtems_attribute attributes; +} rtems_monitor_init_task_t; + + +/* + * Message queue + */ +typedef struct { + rtems_id id; + rtems_name name; + /* end of common portion */ + rtems_attribute attributes; + uint32_t number_of_pending_messages; + uint32_t maximum_pending_messages; + size_t maximum_message_size; +} rtems_monitor_queue_t; + +/* + * Semaphore + */ +typedef struct { + rtems_id id; + rtems_name name; + /* end of common portion */ + rtems_attribute attribute; + rtems_task_priority priority_ceiling; + uint32_t max_count; + uint32_t cur_count; + rtems_id holder_id; +} rtems_monitor_sema_t; + +/* + * Extension + */ +typedef struct { + rtems_id id; + rtems_name name; + /* end of common portion */ + rtems_monitor_symbol_t e_create; + rtems_monitor_symbol_t e_start; + rtems_monitor_symbol_t e_restart; + rtems_monitor_symbol_t e_delete; + rtems_monitor_symbol_t e_tswitch; + rtems_monitor_symbol_t e_begin; + rtems_monitor_symbol_t e_exitted; + rtems_monitor_symbol_t e_fatal; +} rtems_monitor_extension_t; + + /* + * Region + */ +typedef struct { + rtems_id id; + rtems_name name; + /* end of common portion */ + rtems_attribute attribute; + void * start_addr; + uint32_t length; + uint32_t page_size; + uint32_t max_seg_size; + uint32_t used_blocks; +} rtems_monitor_region_t; + +/* + * Partition + */ +typedef struct { + rtems_id id; + rtems_name name; + /* end of common portion */ + rtems_attribute attribute; + void * start_addr; + uint32_t length; + uint32_t buf_size; + uint32_t used_blocks; +} rtems_monitor_part_t; + +/* + * Device driver + */ + +typedef struct { + rtems_id id; /* not really an id (should be tho) */ + rtems_name name; /* ditto */ + /* end of common portion */ + rtems_monitor_symbol_t initialization; /* initialization procedure */ + rtems_monitor_symbol_t open; /* open request procedure */ + rtems_monitor_symbol_t close; /* close request procedure */ + rtems_monitor_symbol_t read; /* read request procedure */ + rtems_monitor_symbol_t write; /* write request procedure */ + rtems_monitor_symbol_t control; /* special functions procedure */ +} rtems_monitor_driver_t; + +/* + * System config + */ + +typedef struct { + void *work_space_start; + uint32_t work_space_size; + uint32_t maximum_tasks; + uint32_t maximum_timers; + uint32_t maximum_semaphores; + uint32_t maximum_message_queues; + uint32_t maximum_partitions; + uint32_t maximum_regions; + uint32_t maximum_ports; + uint32_t maximum_periods; + uint32_t maximum_extensions; + uint32_t microseconds_per_tick; + uint32_t ticks_per_timeslice; + uint32_t number_of_initialization_tasks; +} rtems_monitor_config_t; + +/* + * MPCI config + */ + +#if defined(RTEMS_MULTIPROCESSING) +typedef struct { + uint32_t node; /* local node number */ + uint32_t maximum_nodes; /* maximum # nodes in system */ + uint32_t maximum_global_objects; /* maximum # global objects */ + uint32_t maximum_proxies; /* maximum # proxies */ + + uint32_t default_timeout; /* in ticks */ + size_t maximum_packet_size; + rtems_monitor_symbol_t initialization; + rtems_monitor_symbol_t get_packet; + rtems_monitor_symbol_t return_packet; + rtems_monitor_symbol_t send_packet; + rtems_monitor_symbol_t receive_packet; +} rtems_monitor_mpci_t; +#endif + +/* + * The generic canonical information union + */ + +typedef union { + rtems_monitor_generic_t generic; + rtems_monitor_task_t task; + rtems_monitor_queue_t queue; + rtems_monitor_sema_t sema; + rtems_monitor_extension_t extension; + rtems_monitor_driver_t driver; + rtems_monitor_config_t config; + rtems_monitor_region_t region; + rtems_monitor_part_t part; +#if defined(RTEMS_MULTIPROCESSING) + rtems_monitor_mpci_t mpci; +#endif + rtems_monitor_init_task_t itask; +} rtems_monitor_union_t; + +/* + * Support for talking to other monitors + */ + +/* + * Names of other monitors + */ + +#define RTEMS_MONITOR_NAME (rtems_build_name('R', 'M', 'O', 'N')) +#define RTEMS_MONITOR_SERVER_NAME (rtems_build_name('R', 'M', 'S', 'V')) +#define RTEMS_MONITOR_QUEUE_NAME (rtems_build_name('R', 'M', 'S', 'Q')) +#define RTEMS_MONITOR_RESPONSE_QUEUE_NAME (rtems_build_name('R', 'M', 'R', 'Q')) + +#define RTEMS_MONITOR_SERVER_RESPONSE 0x0001 +#define RTEMS_MONITOR_SERVER_CANONICAL 0x0002 + +typedef struct +{ + uint32_t command; + rtems_id return_id; + uint32_t argument0; + uint32_t argument1; + uint32_t argument2; + uint32_t argument3; + uint32_t argument4; + uint32_t argument5; +} rtems_monitor_server_request_t; + +typedef struct +{ + uint32_t command; + uint32_t result0; + uint32_t result1; + rtems_monitor_union_t payload; +} rtems_monitor_server_response_t; + +extern rtems_id rtems_monitor_task_id; + +extern uint32_t rtems_monitor_node; /* our node number */ +extern uint32_t rtems_monitor_default_node; /* current default for commands */ + +/* + * Monitor command function and table entry + */ + +typedef struct rtems_monitor_command_entry_s rtems_monitor_command_entry_t; +typedef union _rtems_monitor_command_arg_t rtems_monitor_command_arg_t; + +typedef void (*rtems_monitor_command_function_t)( + int argc, + char **argv, + const rtems_monitor_command_arg_t *command_arg, + bool verbose +); + +union _rtems_monitor_command_arg_t { + rtems_monitor_object_type_t monitor_object; + rtems_status_code status_code; + rtems_symbol_table_t **symbol_table; + const rtems_monitor_command_entry_t *monitor_command_entry; +}; + +struct rtems_monitor_command_entry_s { + const char *command; /* command name */ + const char *usage; /* usage string for the command */ + uint32_t arguments_required; /* # of required args */ + rtems_monitor_command_function_t command_function; + /* Some argument for the command */ + rtems_monitor_command_arg_t command_arg; + const rtems_monitor_command_entry_t *next; +}; + + +typedef void *(*rtems_monitor_object_next_fn)(void *, void *, rtems_id *); +typedef void (*rtems_monitor_object_canonical_fn)(void *, void *); +typedef void (*rtems_monitor_object_dump_header_fn)(bool); +typedef void (*rtems_monitor_object_dump_fn)(void *, bool); + +typedef struct { + rtems_monitor_object_type_t type; + void *object_information; + int size; /* of canonical object */ + rtems_monitor_object_next_fn next; + rtems_monitor_object_canonical_fn canonical; + rtems_monitor_object_dump_header_fn dump_header; + rtems_monitor_object_dump_fn dump; +} rtems_monitor_object_info_t; + +typedef bool (*rtems_monitor_per_command_routine)(const rtems_monitor_command_entry_t *, void *); + +/* monitor.c */ +void rtems_monitor_pause_cmd(int, char **, const rtems_monitor_command_arg_t*, bool); +void rtems_monitor_fatal_cmd(int, char **, const rtems_monitor_command_arg_t*, bool); +void rtems_monitor_continue_cmd(int, char **, const rtems_monitor_command_arg_t*, bool); +void rtems_monitor_debugger_cmd(int, char **, const rtems_monitor_command_arg_t*, bool); +void rtems_monitor_reset_cmd(int, char **, const rtems_monitor_command_arg_t*, bool); +void rtems_monitor_node_cmd(int, char **, const rtems_monitor_command_arg_t*, bool); +void rtems_monitor_symbols_loadup(void); +int rtems_monitor_insert_cmd(rtems_monitor_command_entry_t *); +void rtems_monitor_wakeup(void); +void rtems_monitor_command_iterate(rtems_monitor_per_command_routine routine, void *arg); +rtems_status_code rtems_monitor_suspend(rtems_interval timeout); + +/* editor.c */ +void rtems_monitor_kill(void); +void rtems_monitor_init(uint32_t); +void rtems_monitor_task(rtems_task_argument); + +/* server.c */ +void rtems_monitor_server_kill(void); +rtems_status_code rtems_monitor_server_request(uint32_t , rtems_monitor_server_request_t *, rtems_monitor_server_response_t *); +void rtems_monitor_server_task(rtems_task_argument); +void rtems_monitor_server_init(uint32_t ); + +/* command.c */ +int rtems_monitor_make_argv(char *, int *, char **); +int rtems_monitor_command_read(char *, int *, char **); +void rtems_monitor_command_usage(const rtems_monitor_command_entry_t *, const char *); +void rtems_monitor_help_cmd(int, char **, const rtems_monitor_command_arg_t *, bool); +const rtems_monitor_command_entry_t *rtems_monitor_command_lookup(const char *name); + +/* prmisc.c */ +void rtems_monitor_separator(void); +uint32_t rtems_monitor_pad(uint32_t dest_col, uint32_t curr_col); +int rtems_monitor_dump_decimal(uint32_t num); +int rtems_monitor_dump_hex(uint32_t num); +int rtems_monitor_dump_addr(void *addr); +int rtems_monitor_dump_id(rtems_id id); +int rtems_monitor_dump_name(rtems_id id); +int rtems_monitor_dump_priority(rtems_task_priority priority); +int rtems_monitor_dump_state(States_Control state); +int rtems_monitor_dump_modes(rtems_mode modes); +int rtems_monitor_dump_attributes(rtems_attribute attributes); +int rtems_monitor_dump_events(rtems_event_set events); +int rtems_monitor_dump_notepad(uint32_t *notepad); + +/* object.c */ +rtems_id rtems_monitor_id_fixup(rtems_id, uint32_t , rtems_monitor_object_type_t); +const rtems_monitor_object_info_t *rtems_monitor_object_lookup(rtems_monitor_object_type_t type); +rtems_id rtems_monitor_object_canonical_get(rtems_monitor_object_type_t, rtems_id, void *, size_t *size_p); +rtems_id rtems_monitor_object_canonical_next(const rtems_monitor_object_info_t *, rtems_id, void *); +void *rtems_monitor_object_next(void *, void *, rtems_id, rtems_id *); +rtems_id rtems_monitor_object_canonical(rtems_id, void *); +void rtems_monitor_object_cmd(int, char **, const rtems_monitor_command_arg_t*, bool); + +/* manager.c */ +void *rtems_monitor_manager_next(void *, void *, rtems_id *); + +/* config.c */ +void rtems_monitor_config_canonical(rtems_monitor_config_t *, void *); +void *rtems_monitor_config_next(void *, rtems_monitor_config_t *, rtems_id *); +void rtems_monitor_config_dump_header(bool); +int rtems_monitor_config_dump(rtems_monitor_config_t *, bool verbose); + +/* mpci.c */ +#if defined(RTEMS_MULTIPROCESSING) +void rtems_monitor_mpci_canonical(rtems_monitor_mpci_t *, void *); +void *rtems_monitor_mpci_next(void *, rtems_monitor_mpci_t *, rtems_id *); +void rtems_monitor_mpci_dump_header(bool); +void rtems_monitor_mpci_dump(rtems_monitor_mpci_t *, bool verbose); +#endif + +/* itask.c */ +void rtems_monitor_init_task_canonical(rtems_monitor_init_task_t *, void *); +void *rtems_monitor_init_task_next(void *, rtems_monitor_init_task_t *, rtems_id *); +void rtems_monitor_init_task_dump_header(bool); +void rtems_monitor_init_task_dump(rtems_monitor_init_task_t *, bool verbose); + +/* extension.c */ +void rtems_monitor_extension_canonical(rtems_monitor_extension_t *, void *); +void rtems_monitor_extension_dump_header(bool verbose); +void rtems_monitor_extension_dump(rtems_monitor_extension_t *, bool); + +/* task.c */ +void rtems_monitor_task_canonical(rtems_monitor_task_t *, void *); +void rtems_monitor_task_dump_header(bool verbose); +void rtems_monitor_task_dump(rtems_monitor_task_t *, bool); + +/* sema.c */ +void rtems_monitor_sema_canonical(rtems_monitor_sema_t *, void *); +void rtems_monitor_sema_dump_header(bool verbose); +void rtems_monitor_sema_dump(rtems_monitor_sema_t *, bool); + +/* queue.c */ +void rtems_monitor_queue_canonical(rtems_monitor_queue_t *, void *); +void rtems_monitor_queue_dump_header(bool verbose); +void rtems_monitor_queue_dump(rtems_monitor_queue_t *, bool); + +/* region.c */ +void rtems_monitor_region_canonical(rtems_monitor_region_t *, void *); +void rtems_monitor_region_dump_header(bool verbose); +void rtems_monitor_region_dump(rtems_monitor_region_t *, bool); + +/* partition.c */ +void rtems_monitor_part_canonical(rtems_monitor_part_t *, void *); +void rtems_monitor_part_dump_header(bool verbose); +void rtems_monitor_part_dump(rtems_monitor_part_t *, bool); + +/* driver.c */ +void *rtems_monitor_driver_next(void *, rtems_monitor_driver_t *, rtems_id *); +void rtems_monitor_driver_canonical(rtems_monitor_driver_t *, void *); +void rtems_monitor_driver_dump_header(bool); +void rtems_monitor_driver_dump(rtems_monitor_driver_t *, bool); + +/* symbols.c */ +rtems_symbol_table_t *rtems_symbol_table_create(void); +void rtems_symbol_table_destroy(rtems_symbol_table_t *table); + +rtems_symbol_t *rtems_symbol_create(rtems_symbol_table_t *, const char *, uint32_t ); +rtems_symbol_t *rtems_symbol_value_lookup(rtems_symbol_table_t *, uint32_t ); +const rtems_symbol_t *rtems_symbol_value_lookup_exact(rtems_symbol_table_t *, uint32_t ); +rtems_symbol_t *rtems_symbol_name_lookup(rtems_symbol_table_t *, const char *); +void *rtems_monitor_symbol_next(void *object_info, rtems_monitor_symbol_t *, rtems_id *); +void rtems_monitor_symbol_canonical(rtems_monitor_symbol_t *, rtems_symbol_t *); +void rtems_monitor_symbol_canonical_by_name(rtems_monitor_symbol_t *, const char *); +void rtems_monitor_symbol_canonical_by_value(rtems_monitor_symbol_t *, void *); +uint32_t rtems_monitor_symbol_dump(rtems_monitor_symbol_t *, bool); +void rtems_monitor_symbol_cmd(int, char **, const rtems_monitor_command_arg_t*, bool); + +#if defined(RTEMS_NETWORKING) +void mon_ifconfig( + int argc, + char *argv[], + uint32_t command_arg, + bool verbose +); +void mon_route( + int argc, + char *argv[], + uint32_t command_arg, + bool verbose +); +#endif + +/* mon-object.c */ +const rtems_monitor_object_info_t *rtems_monitor_object_lookup( + rtems_monitor_object_type_t type +); + +/* shared data */ +extern rtems_symbol_table_t *rtems_monitor_symbols; + +#define MONITOR_WAKEUP_EVENT RTEMS_EVENT_0 + +#ifdef __cplusplus +} +#endif + +#endif /* ! __MONITOR_H */ diff --git a/cpukit/libmisc/monitor/symbols.h b/cpukit/libmisc/monitor/symbols.h new file mode 100644 index 0000000000..6246379a94 --- /dev/null +++ b/cpukit/libmisc/monitor/symbols.h @@ -0,0 +1,64 @@ +/* + * RTEMS monitor symbol table functions + * + * Description: + * Entry points for symbol table routines. + * + * + * + * TODO: + * + * $Id$ + */ + +#ifndef _INCLUDE_SYMBOLS_H +#define _INCLUDE_SYMBOLS_H + +#include <rtems/monitor.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct _rtems_symbol_t { + uint32_t value; + char *name; +} ; + +#define SYMBOL_STRING_BLOCK_SIZE 4080 +typedef struct rtems_symbol_string_block_s { + struct rtems_symbol_string_block_s *next; + char buffer[SYMBOL_STRING_BLOCK_SIZE]; +} rtems_symbol_string_block_t; + +struct _rtems_symbol_table_t { + + uint32_t sorted; /* are symbols sorted right now? */ + uint32_t growth_factor; /* % to grow by when needed */ + uint32_t next; /* next symbol slot to use when adding */ + uint32_t size; /* max # of symbols */ + + /* + * Symbol list -- sorted by address (when we do a lookup) + */ + + rtems_symbol_t *addresses; /* symbol array by address */ + + /* + * String pool, unsorted, a list of blocks of string data + */ + + rtems_symbol_string_block_t *string_buffer_head; + rtems_symbol_string_block_t *string_buffer_current; + uint32_t strings_next; /* next byte to use in this block */ + +} ; + +#define rtems_symbol_name(sp) ((sp)->name) +#define rtems_symbol_value(sp) ((sp)->value) + +#ifdef __cplusplus +} +#endif + +#endif /* ! _INCLUDE_SYMBOLS_H */ diff --git a/cpukit/libmisc/mouse/README b/cpukit/libmisc/mouse/README new file mode 100644 index 0000000000..ab684a6d0b --- /dev/null +++ b/cpukit/libmisc/mouse/README @@ -0,0 +1,214 @@ +# +# $Id$ +# + +SOURCE: http://www.kryslix.com/nsfaq/Q.12.html + +Subject: What protocol do mice use? +Date: 09/16/97 +Updated: 11/16/00 This document was adapated from a web page produced by +Tomi Engdahl <then@delta.hut.fi> +Microsoft serial mouse +Description + +The Microsoft serial mouse is the most popular 2 button mouse. It is +supported by all major operating systems. The maximum tracking rate for +a Microsoft mouse is 40 reports/second * 127 counts per report, in other +words, 5080 counts per second. The most common range for mice is is 100 +to 400 CPI (counts per inch) but can be up to 1000 CPI. A 100CPI mouse +can discriminate motion up to 50.8 inches/second while a 400 CPI mouse +can only discriminate motion up to 12.7 inches/second. +Pinout + +9 pin 25 pin Line Comments +shell 1 GND +3 2 TD Serial data from host to mouse (only for power) +2 3 RD Serial data from mouse to host +7 4 RTS Positive voltage to mouse +8 5 CTS +6 6 DSR +5 7 SGND +4 20 DTR Positive voltage to mouse and reset/detection + +RTS = Request to Send CTS = Clear to Send +DSR = Data Set Ready DTR = Data Terminal Ready +GND = Protective Ground SGND = Signal Ground + +To function correctly, both the RTS and DTR lines must be +positive. DTR/DSR and RTS/CTS must NOT be shorted. RTS may be toggled +negative for at least 100ms to reset the mouse. (After a cold boot, the +RTS line is usually negative. This provides an automatic toggle when +RTS is brought positive). When DTR is toggled the mouse should send a +single byte 0x45 (ASCII 'M'). + +Serial data parameters: +1200bps, 7 databits, 1 stop-bit + +Data packet format: + +Data is sent in 3 byte packets for each event (a button is pressed or +released or the mouse moves): + + D7 D6 D5 D4 D3 D2 D1 D0 + +Byte 1 X 1 LB RB Y7 Y6 X7 X6 +Byte 2 X 0 X5 X4 X3 X2 X1 X0 +Byte 3 X 0 Y5 Y4 Y3 Y2 Y1 Y0 + +LB is the state of the left button (1 means down) +RB is the state of the right button (1 means down) +X7-X0 movement in X direction since last packet (signed byte) +Y7-Y0 movement in Y direction since last packet (signed byte) + +The high order bit of each byte (D7) is ignored. Bit D6 indicates the start of an event, which allows the software to synchronize with the mouse. + +Graphical representation of a packet + + 1st byte 2nd byte 3rd byte + ================ =============== ================ + - 1 ? ? Y Y X X - 0 X X X X X X - 0 Y Y Y Y Y Y + ================ =============== ================ + | | \ / \ / \---------/ \---------/ + | | | | | | + | | | \----\ | | + | | \--------|-------|--------\ | + | | / \ /---------\ / \ /---------\ + | | ================ ================= + | | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + Left Button --/ | ================ ================= +Right Button ----/ X increment Y increment + +3 Button Logitech extension + +Logitech extended the 2 button mouse protocol to support 3 button mice +by adding a 4th byte when the middle button is pressed (and the first +packet after it is released). If a 4th byte is encountered (i.e., an +extra byte with D6 set to 0) then D5 of that byte (0x20) indicates the +status of the middle mouse button. + +Mouse systems mouse + +Serial data parameters: + +1200bps, 8 databits, 1 stop-bit + +5 byte Mouse Systems packet + + D7 D6 D5 D4 D3 D2 D1 D0 + +Byte 1 1 0 0 0 0 LB CB RB +Byte 2 X7 X6 X5 X4 X3 X2 X1 X0 +Byte 3 Y7 Y6 Y5 Y4 Y3 Y4 Y1 Y0 +Byte 4 X7' X6' X5' X4' X3' X2' X1' X0' +Byte 5 Y7' Y6' Y5' Y4' Y3' Y4' Y1' Y0' + +LB is left button state (0=pressed, 1=released) +CB is center button state (0=pressed, 1=released) +RB is right button state (0=pressed, 1=released) +X7-X0 movement in X direction since last packet in signed byte + format (-128..+127), positive direction right +Y7-Y0 movement in Y direction since last packet in signed byte + format (-128..+127), positive direction up +X7'-X0' movement in X direction since sending of X7-X0 packet in signed byte + format (-128..+127), positive direction right +Y7'-Y0' movement in Y direction since sending of Y7-Y0 in signed byte + format (-128..+127), positive direction up + +The last two bytes in the packet (bytes 4 and 5) contain information +about movement data changes which have occured after data bytes 2 and +3 have been sent. + +PS/2 mouse + +The standard PS/2 mouse (such as the Logitech mouse) defaults to 160 CPI +and can be switched to 40, 80, 160 or 320 CPI via software. The Microsoft +mouse driver for Windows 3.x and Windows 95 defaults to 160 counts per +inch. The maximum tracking rate for PS/2 mouse is 40 reports/second * +255 counts per report, or 10200 counts per second. A 100 CPI mouse could +discriminate motion up to 102 inches per second while a 400 CPI mouse +could discriminate motion up to 25.2 inches per second. + +Connector pinout + +Pin Wire Name +1 DATA +2 Reserved +3 Ground +4 +5V Supply +5 CLK +6 Reserved +Shield Chassis + +Packet Format + + D7 D6 D5 D4 D3 D2 D1 D0 +Byte 1 XV XV YS XS 1 M R L +Byte 2 X7 X6 X5 X4 X3 X2 X1 X0 +Byte 3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 + +L Left button state (1 = pressed down) +M Middle button state (1 = pressed down) +R Right button state (1 = pressed down) +X0-X7 Movement in X direction +Y0-Y7 Movement in Y direction +XS,YS Movement data sign bits (1 = negative) +XV,YV Movement data overflow bits (1 = overflow has occured) + +Physical connector + +The PS/2 mouse connector has the following pinout when looking at the +connector on the back of the computer: + + 4 u 6 + 1 . 2 + 3 5 + +1. GND +2. +5V +3. DATA +4. CLOCK +5. Not used +6. Not used + +Bi-directional transmission is controlled by the CLK and DATA lines. Both +are fed by an open collector device which lets either host or mouse force +the line to "0". During non-transmission, CLK is at "1" and DATA can be at +"0" or "1". + +The host can inhibit mouse transmission by forcing CLK to "0". If the +host inhibits the mouse while it is transmitting, the byte must be +retransmitted (if the inhibit state arrived before the 11th clock). + +Receiving data: Check 'clock'. If inactive, there is a bit on the 'data' +line. Each transmission unit is one start bit, eight data bits, odd +parity and one stop bit. Start bits are low, stop bits high. Each clock +active or inactive period is 30 to 50 microseconds. Data transition to +falling edge of clock is 5 to 25 microseconds. + +Sending: Check that both clock and data are high. Pull down data for +start bit, and start clocking. + +NOTE: Logitech has made the extension to this protocol to allow three +buttons (the M bit is always 0 on 2 button mice). + +References + +* Original PC mouse info page by Tomi Engdahl + http://www.hut.fi/~then/mytexts/mouse.html + +* PC Magazine May 28, 1991 + +* Programmer's Reference to Genius Mouse + ftp://x2ftp.oulu.fi/pub/msdos/programming/docs/gmouse.doc + +* Logitech Pointing Device Hardware Information Product Support Document # 1410 + ftp://ftp.logitech.com/pub/TechSupport/MOUSE/HELP/1410.txt + +* Mice: How do they work? by Richard Torrens + http://box.argonet.co.uk/users/4qd/meece.html + +* In Pursuit Of The Perfect Portable Pointer by Intelink Electronics +from EDN Products Edition April 16, 1977 pages 43-45 + +* Programming the Microsoft Mouse + http://www.geocities.com/SiliconValley/2151/mouse.html diff --git a/cpukit/libmisc/mouse/mouse_parser.c b/cpukit/libmisc/mouse/mouse_parser.c new file mode 100644 index 0000000000..9d78bbd979 --- /dev/null +++ b/cpukit/libmisc/mouse/mouse_parser.c @@ -0,0 +1,311 @@ +/* + * This code is derived from a UNIX Serial Port Mouse Driver with + * the following notice: + * + * ================================================================== + * Copyright (c) 1999 Greg Haerr <greg@censoft.com> + * Portions Copyright (c) 1991 David I. Bell + * Permission is granted to use, distribute, or modify this source, + * provided that this copyright notice remains intact. + * + * UNIX Serial Port Mouse Driver + * + * This driver opens a serial port directly, and interprets serial data. + * Microsoft, PC, Logitech and PS/2 mice are supported. The PS/2 mouse + * is only supported if the OS runs the mouse byte codes through the + * serial port. + * + * Mouse Types Supported: pc ms, logi, ps2 + * ================================================================== + * + * It has been modified to support the concept of being just a parser + * fed data from an arbitrary source. It is independent of either + * a PS/2 driver or a serial port. + * + * It was moved to cpukit/libmisc/mouse by Joel Sherrill. + * + * $Id$ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> /* strcmp */ +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/mouse_parser.h> +#include <rtems/mw_uid.h> + +/* states for the mouse */ +#define IDLE 0 /* start of byte sequence */ +#define XSET 1 /* setting x delta */ +#define YSET 2 /* setting y delta */ +#define XADD 3 /* adjusting x delta */ +#define YADD 4 /* adjusting y delta */ + +/* values in the bytes returned by the mouse for the buttons*/ +#define PC_LEFT_BUTTON 4 +#define PC_MIDDLE_BUTTON 2 +#define PC_RIGHT_BUTTON 1 + +#define MS_LEFT_BUTTON 2 +#define MS_RIGHT_BUTTON 1 + +#define PS2_CTRL_BYTE 0x08 +#define PS2_LEFT_BUTTON 1 +#define PS2_RIGHT_BUTTON 2 + +/* Bit fields in the bytes sent by the mouse.*/ +#define TOP_FIVE_BITS 0xf8 +#define BOTTOM_THREE_BITS 0x07 +#define TOP_BIT 0x80 +#define SIXTH_BIT 0x40 +#define BOTTOM_TWO_BITS 0x03 +#define THIRD_FOURTH_BITS 0x0c +#define BOTTOM_SIX_BITS 0x3f + +/* local data */ +static int state; /* IDLE, XSET, ... */ +static BUTTON buttons; /* current mouse buttons pressed*/ +static BUTTON availbuttons; /* which buttons are available */ +static COORD xd; /* change in x */ +static COORD yd; /* change in y */ + +static int left; /* because the button values change */ +static int middle; /* between mice, the buttons are */ +static int right; /* redefined */ + +static int (*parse)( int ); /* parse routine */ + +/* local routines*/ +static int ParsePC(int); /* routine to interpret PC mouse */ +static int ParseMS(int); /* routine to interpret MS mouse */ +static int ParsePS2(int); /* routine to interpret PS/2 mouse */ + +/* + * Open up the mouse device. + * Returns the fd if successful, or negative if unsuccessful. + */ +int mouse_parser_initialize(const char *type) +{ + /* set button bits and parse procedure*/ + if (!strcmp(type, "pc") || !strcmp(type, "logi")) { + /* pc or logitech mouse*/ + left = PC_LEFT_BUTTON; + middle = PC_MIDDLE_BUTTON; + right = PC_RIGHT_BUTTON; + parse = ParsePC; + } else if (strcmp(type, "ms") == 0) { + /* microsoft mouse*/ + left = MS_LEFT_BUTTON; + right = MS_RIGHT_BUTTON; + middle = 0; + parse = ParseMS; + } else if (strcmp(type, "ps2") == 0) { + /* PS/2 mouse*/ + left = PS2_LEFT_BUTTON; + right = PS2_RIGHT_BUTTON; + middle = 0; + parse = ParsePS2; + } else + return -1; + + printk("Device: /dev/mouse -- mouse type is: %s\n", type ); + + /* initialize data*/ + availbuttons = left | middle | right; + state = IDLE; + buttons = 0; + xd = 0; + yd = 0; + return 0; +} + +/* + * Attempt to read bytes from the mouse and interpret them. + * Returns -1 on error, 0 if either no bytes were read or not enough + * was read for a complete state, or 1 if the new state was read. + * When a new state is read, the current buttons and x and y deltas + * are returned. This routine does not block. + */ +int MOU_Data( int ch, COORD *dx, COORD *dy, COORD *dz, BUTTON *bptr) +{ + int b; + + if ( !parse ) { + printk( "Mouse parser is not initialized!\n" ); + return -1; + } + + /* + * Loop over all the bytes read in the buffer, parsing them. + * When a complete state has been read, return the results, + * leaving further bytes in the buffer for later calls. + */ + if ( (*parse)( ch ) ) { + *dx = xd; + *dy = yd; + *dz = 0; + b = 0; + if (buttons & left) + b |= LBUTTON; + if (buttons & right) + b |= RBUTTON; + if (buttons & middle) + b |= MBUTTON; + *bptr = b; + return 1; + } + return 0; +} + +/* + * Input routine for PC mouse. + * Returns nonzero when a new mouse state has been completed. + */ +static int ParsePC(int byte) +{ + int sign; /* sign of movement */ + + switch (state) { + case IDLE: + if ((byte & TOP_FIVE_BITS) == TOP_BIT) { + buttons = ~byte & BOTTOM_THREE_BITS; + state = XSET; + } + break; + + case XSET: + sign = 1; + if (byte > 127) { + byte = 256 - byte; + sign = -1; + } + xd = byte * sign; + state = YSET; + break; + + case YSET: + sign = 1; + if (byte > 127) { + byte = 256 - byte; + sign = -1; + } + yd = -byte * sign; + state = XADD; + break; + + case XADD: + sign = 1; + if (byte > 127) { + byte = 256 - byte; + sign = -1; + } + xd += byte * sign; + state = YADD; + break; + + case YADD: + sign = 1; + if (byte > 127) { + byte = 256 - byte; + sign = -1; + } + yd -= byte * sign; + state = IDLE; + return 1; + } + return 0; +} + +/* + * Input routine for Microsoft mouse. + * Returns nonzero when a new mouse state has been completed. + */ +static int ParseMS(int byte) +{ + switch (state) { + case IDLE: + if (byte & SIXTH_BIT) { + buttons = (byte >> 4) & BOTTOM_TWO_BITS; + yd = ((byte & THIRD_FOURTH_BITS) << 4); + xd = ((byte & BOTTOM_TWO_BITS) << 6); + state = XADD; + } + break; + + case XADD: + xd |= (byte & BOTTOM_SIX_BITS); + state = YADD; + break; + + case YADD: + yd |= (byte & BOTTOM_SIX_BITS); + state = IDLE; + if (xd > 127) + xd -= 256; + if (yd > 127) + yd -= 256; + return 1; + } + return 0; +} + +/* + * Input routine for PS/2 mouse. + * Returns nonzero when a new mouse state has been completed. + */ +static int ParsePS2(int byte) +{ + switch (state) { + case IDLE: + if (byte & PS2_CTRL_BYTE) { + buttons = byte & + (PS2_LEFT_BUTTON|PS2_RIGHT_BUTTON); + state = XSET; + } + break; + + case XSET: + if(byte > 127) + byte -= 256; + xd = byte; + state = YSET; + break; + + case YSET: + if(byte > 127) + byte -= 256; + yd = -byte; + state = IDLE; + return 1; + } + return 0; +} + +/* generic mouse parser */ +void mouse_parser_enqueue( unsigned char *buffer, size_t size ) +{ + COORD dx; + COORD dy; + COORD dz; + BUTTON bptr; + + while( size-- ) { + if ( MOU_Data( *buffer++, &dx, &dy, &dz, &bptr ) ) { + struct MW_UID_MESSAGE m; + + m.type = MV_UID_REL_POS; + /* buttons definitons have been selected to match */ + m.m.pos.btns = bptr; + m.m.pos.x = dx; + m.m.pos.y = dy; + m.m.pos.z = dz; + /* printk( "Mouse: msg: dx=%d, dy=%d, btn=%X\n", dx, dy, bptr ); */ + uid_send_message( &m ); + } + } +} + diff --git a/cpukit/libmisc/mouse/mouse_parser.h b/cpukit/libmisc/mouse/mouse_parser.h new file mode 100644 index 0000000000..59b6abb2f3 --- /dev/null +++ b/cpukit/libmisc/mouse/mouse_parser.h @@ -0,0 +1,108 @@ +/* + * This file is the header file for the Mouse Parser Engine which + * is derived from a UNIX Serial Port Mouse Driver with the following + * notice: + * + * ================================================================== + * Copyright (c) 1999 Greg Haerr <greg@censoft.com> + * Portions Copyright (c) 1991 David I. Bell + * Permission is granted to use, distribute, or modify this source, + * provided that this copyright notice remains intact. + * + * UNIX Serial Port Mouse Driver + * + * This driver opens a serial port directly, and interprets serial data. + * Microsoft, PC, Logitech and PS/2 mice are supported. The PS/2 mouse + * is only supported if the OS runs the mouse byte codes through the + * serial port. + * + * Mouse Types Supported: pc ms, logi, ps2 + * ================================================================== + * + * It has been modified to support the concept of being just a parser + * fed data from an arbitrary source. It is independent of either + * a PS/2 driver or a serial port. + * + * It was moved to cpukit/libmisc/mouse by Joel Sherrill. + * + * $Id$ + */ + +#ifndef __MOUSE_PARSER_h__ +#define __MOUSE_PARSER_h__ + +#include <rtems/mw_uid.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * This is the mask for the right button. + * + * @note Use the same definitions as the user interface. + */ +#define RBUTTON MV_BUTTON_RIGHT + +/** + * This is the mask for the center button. + * + * @note Use the same definitions as the user interface. + */ +#define MBUTTON MV_BUTTON_CENTER + +/** + * This is the mask for the left button. + * + * @note Use the same definitions as the user interface. + */ +#define LBUTTON MV_BUTTON_LEFT + +/** + * This type is the device coordinates. + */ +typedef int COORD; + +/** + * This type is the mouse button mask. + */ +typedef unsigned int BUTTON; + +/** + * This type defines a pointer to the enqueue method. It is + * available since some device drivers keep pointers to the method + * to know when to enqueue or not. + */ +typedef void (*mouse_parser_enqueue_handler)(unsigned char *, size_t); + +/** + * @brief Initialize the Mouse Parser Engine + * + * This method initializes the Mouse Parser Engine for the mouse + * of @a type. The @a type should be one of the following strings: + * pc ms, logi, ps2. + * + * @a param[in] type indicates the type of mouse. + * + * @return This method returns 0 on success and -1 on error. + */ +int mouse_parser_initialize(const char *type); + +/** + * @brief Enqueue Input to the Mouse Parser Engine + * + * This method is used to pass mouse input to the Mouse Parser Engine. + * + * @a param[in] buffer is the data to enqueue + * @a param[in] size is the amount of data to enqueue + */ +void mouse_parser_enqueue( + unsigned char *buffer, + size_t size +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cpukit/libmisc/mouse/serial_mouse.c b/cpukit/libmisc/mouse/serial_mouse.c new file mode 100644 index 0000000000..c61401626d --- /dev/null +++ b/cpukit/libmisc/mouse/serial_mouse.c @@ -0,0 +1,184 @@ +/* + * COPYRIGHT (c) 1989-2011. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <rtems/libio.h> +#include <termios.h> +#include <rtems/termiostypes.h> +#include <rtems/mouse_parser.h> +#include <rtems/serial_mouse.h> + +int serial_mouse_fd = -1; +const char *serial_mouse_device; +const char *serial_mouse_type; + +int serial_mouse_l_rint(int c, struct rtems_termios_tty *tp) +{ + unsigned char buf = c; + + /* call mouse_parser( void *ptr, char *buffer, int size ) */ + mouse_parser_enqueue( &buf, 1 ); + return 0; +} + +static struct rtems_termios_linesw serial_mouse_linesw = { + .l_open = NULL, + .l_close = NULL, + .l_read = NULL, + .l_write = NULL, + .l_rint = serial_mouse_l_rint, + .l_start = NULL, + .l_ioctl = NULL, + .l_modem = NULL +}; + + +/* + * Serial Mouse - device driver INITIALIZE entry point. + */ +rtems_device_driver serial_mouse_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + bool status; + + status = bsp_get_serial_mouse_device( + &serial_mouse_device, + &serial_mouse_type + ); + + (void) rtems_io_register_name( "/dev/mouse", major, 0 ); + + rtems_termios_linesw[ 6 ] = serial_mouse_linesw; + + return RTEMS_SUCCESSFUL; +} + +/* + * serial_mouse - device driver OPEN entry point + */ +rtems_device_driver serial_mouse_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *args +) +{ + struct termios termios_attr; + int status; + int disc = 6; + + /* XXX open(2) the configured /dev/comX */ + /* XXX save the file descriptor */ + serial_mouse_fd = open( serial_mouse_device, O_RDONLY ); + if ( serial_mouse_fd == -1 ) { + printk( + "Error opening serial_mouse device on %s\n", + serial_mouse_device + ); + return RTEMS_IO_ERROR; + } + + /* 1200-8-N-1, without hardware flow control */ + /* BSP_uart_init( BSP_UART_PORT, 1200, CHR_8_BITS, 0, 0, 0 ); */ + status = tcgetattr(serial_mouse_fd, &termios_attr ); + if (status != 0) { + printk("Error getting mouse attributes\n"); + return RTEMS_IO_ERROR; + } + termios_attr.c_lflag &= ~(ICANON|ECHO|ECHONL|ECHOK|ECHOE|ECHOPRT|ECHOCTL); + termios_attr.c_iflag &= ~(IXON|IXANY|IXOFF); + /* + termios_attr.c_cc[VMIN] = itask_VMIN; + termios_attr.c_cc[VTIME] = itask_VTIME; + */ + termios_attr.c_cflag |= B1200; + termios_attr.c_cflag |= CS8; + status = tcsetattr( serial_mouse_fd, TCSANOW, &termios_attr ); + if (status != 0) { + printk("Error setting mouse attributes\n"); + return RTEMS_IO_ERROR; + } + + status = ioctl(serial_mouse_fd, TIOCSETD, &disc); + if (status != 0) { + printk("Error setting mouse attributes\n"); + return RTEMS_IO_ERROR; + } + + sleep(5); + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver serial_mouse_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + close( serial_mouse_fd ); + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver serial_mouse_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return RTEMS_SUCCESSFUL; +} + + +rtems_device_driver serial_mouse_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return RTEMS_SUCCESSFUL; +} + + +rtems_device_driver serial_mouse_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_ioctl_args_t *args = (rtems_libio_ioctl_args_t *)arg; + + switch( args->command ) { + + case MW_UID_REGISTER_DEVICE: + printk( "SerialMouse: reg=%s\n", args->buffer ); + mouse_parser_initialize( serial_mouse_type ); + break; + + case MW_UID_UNREGISTER_DEVICE: + break; + + default: + args->ioctl_return = ioctl(serial_mouse_fd, args->command, args->buffer ); + if ( !args->ioctl_return ) + return RTEMS_SUCCESSFUL; + return RTEMS_IO_ERROR; + } + args->ioctl_return = 0; + return RTEMS_SUCCESSFUL; +} diff --git a/cpukit/libmisc/mouse/serial_mouse.h b/cpukit/libmisc/mouse/serial_mouse.h new file mode 100644 index 0000000000..65e98089cb --- /dev/null +++ b/cpukit/libmisc/mouse/serial_mouse.h @@ -0,0 +1,156 @@ +/** + * @file rtems/serial_mouse.h + * + * This file describes the Serial Mouse Driver for all boards. + * This driver assumes that the BSP or application will provide + * an implementation of the method bsp_get_serial_mouse_device() + * which tells the driver what serial port device to open() and + * what type of mouse is connected. + * + * This driver relies on the Mouse Parser Engine. + */ + +/* + * COPYRIGHT (c) 1989-2011. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef __SERIAL_MOUSE_h__ +#define __SERIAL_MOUSE_h__ + +/* functions */ +#ifdef __cplusplus +extern "C" { +#endif + +/** + * This macro defines the serial mouse device driver entry points. + */ +#define SERIAL_MOUSE_DRIVER_TABLE_ENTRY \ + { serial_mouse_initialize, serial_mouse_open, serial_mouse_close, \ + serial_mouse_read, serial_mouse_write, serial_mouse_control } + +/** + * @brief Serial Mouse Driver Initialization + * + * This method initializes the serial mouse driver. + * + * @param[in] major is the mouse device major number + * @param[in] minor is the mouse device minor number + * @param[in] arguments points to device driver arguments + */ +rtems_device_driver serial_mouse_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +); + +/** + * @brief Serial Mouse Driver Open + * + * This method implements the Open device driver entry + * point for the serial mouse driver. + * + * @param[in] major is the mouse device major number + * @param[in] minor is the mouse device minor number + * @param[in] arguments points to device driver arguments + */ +rtems_device_driver serial_mouse_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +); + +/** + * @brief Serial Mouse Driver Close + * + * This method implements the Close device driver entry + * point for the serial mouse driver. + * + * @param[in] major is the mouse device major number + * @param[in] minor is the mouse device minor number + * @param[in] arguments points to device driver arguments + */ +rtems_device_driver serial_mouse_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +); + +/** + * @brief Serial Mouse Driver Read + * + * This method implements the Read device driver entry + * point for the serial mouse driver. + * + * @param[in] major is the mouse device major number + * @param[in] minor is the mouse device minor number + * @param[in] arguments points to device driver arguments + */ +rtems_device_driver serial_mouse_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +); + +/** + * @brief Serial Mouse Driver Write + * + * This method implements the Write device driver entry + * point for the serial mouse driver. + * + * @param[in] major is the mouse device major number + * @param[in] minor is the mouse device minor number + * @param[in] arguments points to device driver arguments + */ +rtems_device_driver serial_mouse_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +); + +/** + * @brief Serial Mouse Driver IO Control + * + * This method implements the IO Control device driver entry + * point for the serial mouse driver. + * + * @param[in] major is the mouse device major number + * @param[in] minor is the mouse device minor number + * @param[in] arguments points to device driver arguments + */ +rtems_device_driver serial_mouse_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +); + +/** + * @brief Obtain Serial Mouse Configuration Information + * + * This method is implemented by the BSP or application and + * tells the driver what device to open() and what type of + * mouse is connected. + * + * @param[in] name will point to a string with the device name + * of the serial port with the mouse connected. + * @param[in] type will point to a string with the type of mouse connected. + * + * @return This method returns true on success and false on error. + */ +bool bsp_get_serial_mouse_device( + const char **name, + const char **type +); + +#ifdef __cplusplus +} +#endif + +#endif /* __tty_drv__ */ diff --git a/cpukit/libmisc/preinstall.am b/cpukit/libmisc/preinstall.am new file mode 100644 index 0000000000..f6a7e763d2 --- /dev/null +++ b/cpukit/libmisc/preinstall.am @@ -0,0 +1,9 @@ +## Automatically generated by ampolish3 - Do not edit + +if AMPOLISH3 +$(srcdir)/preinstall.am: Makefile.am + $(AMPOLISH3) $(srcdir)/Makefile.am > $(srcdir)/preinstall.am +endif + +if LIBSHELL +endif diff --git a/cpukit/libmisc/serdbg/README b/cpukit/libmisc/serdbg/README new file mode 100644 index 0000000000..338249c928 --- /dev/null +++ b/cpukit/libmisc/serdbg/README @@ -0,0 +1,138 @@ +# +# $Id$ +# + +This directory contains three useful packages related to the termios I/O +system: + +PACKAGE SERDBGIO +================ +"serdbgio" provides the "serial gdb" standard I/O functions "getDebugChar" +and "putDebugChar" for any device driver supporting polled termios mode. + +The initialization function "serdbg_open" opens the v.24 port intended +for the serial debug connection, and sets the desired baud rate. The +"getDebugChar" and "putDebugChar" functions then interact with the +corresponding driver using the calls intended for polled termios +operation. + +Specification for the debug device, baud rate and other parameters is +done in a global structure of type "serdbg_conf_t". A configuration +mechanism quite similar to the overall RTEMS configuration is available. + +PACKAGE SERDBG +============== +"serdbg" provides a means to optionally initialize and/or start a +serial gdb session as soon as possible, this means as soon as all +drivers have been initialized. The serial debug I/O functions can +either be integrated as special routines of the BSP drivers, or using +the package "serdbgio" + +PACKAGE TERMIOS_PRINTK +====================== +"termios_printk" provides a standard output function suitable to use +with "printk". It uses the same technique as serdbgio, hooking the +interface between a polled device driver and the termios system. + + +REQUIREMENTS +============ + +- These two packages can be used with any polled termios device +driver. +- For standard initialization, they need a modified "bsppost.c" +to perform the initialization calls. + +USAGE +===== + +For using these packages add the following to your "init" module or +your "system.h" file (Note: most macro settings fall back to a +default, if not set.): + +/* + * CONFIGURE_USE_SERDBG + * set this macro, if you want to connect gdb over a serial line + * when set, the debug stub will be connected after driver + * initialization in "bsppost.c" + */ +#define CONFIGURE_USE_SERDBG + + +/* + * CONFIGURE_SERDBG_SKIP_INIT_BKPT + * set this macro, if you do not want the gdb interface to wait for a + * debugger connection directly after initialization + * If you set this macro, the gdb stub will only hook various + * exception vectors when called from "bsppost.c". + */ +/* #define CONFIGURE_SERDBG_SKIP_INIT_BKPT */ + +/* + * CONFIGURE_SERDBG_USE_POLLED_TERMIOS + * set this macro, if you want "serdbgio" to provide the I/O + * functions for the serial gdb connection + */ +#define CONFIGURE_SERDBG_USE_POLLED_TERMIOS + +/* + * CONFIGURE_SERDBG_DEVNAME + * use this macro to specify the serial device to use + * for "serdbgio". + * Only used, when CONFIGURE_SERDBG_USE_POLLED_TERMIOS is set + */ +#define CONFIGURE_SERDBG_DEVNAME "/dev/tty03" + +/* + * CONFIGURE_SERDBG_BAUDRATE + * use this macro to specify the baud rate to use + * for "serdbgio". + * Only used, when CONFIGURE_SERDBG_USE_POLLED_TERMIOS is set + */ +#define CONFIGURE_SERDBG_BAUDRATE 57600 + +/* + * CONFIGURE_SERDBG_CALLOUT + * use this macro to specify a routine that will called during I/O polling + * Only used, when CONFIGURE_SERDBG_USE_POLLED_TERMIOS is set + * This function of type "void pollfnc(void)" can be used for e.g. + * tickling a watchdog + */ +/* #define CONFIGURE_SERDBG_CALLOUT tickle_my_watchdog_fnc */ + +#include <serdbgcnf.h> + +/* + * CONFIGURE_USE_TERMIOS_PRINTK + * set this macro, if you want printk output to be sent to a serial + * driver using the polled termios interface + * when set, the printk output function will be connected after driver + * initialization in "bsppost.c" + */ +#define CONFIGURE_USE_TERMIOS_PRINTK + +/* + * CONFIGURE_TERMIOS_PRINTK_DEVNAME + * use this macro to specify the serial device to use + * for printk output. + * Only used, when CONFIGURE_USE_TERMIOS_PRINTK is set + */ +#define CONFIGURE_TERMIOS_PRINTK_DEVNAME "/dev/console" + +/* + * CONFIGURE_TERMIOS_PRINTK_BAUDRATE + * use this macro to specify the baudrate to use + * for printk output. + * Only used, when CONFIGURE_USE_TERMIOS_PRINTK is set + */ +#define CONFIGURE_TERMIOS_PRINTK_BAUDRATE 9600 + +/* + * CONFIGURE_TERMIOS_PRINTK_CALLOUT + * use this macro to specify a routine that will called during I/O polling + * This function of type "void pollfnc(void)" can be used for e.g. + * tickling a watchdog + */ +/* #define CONFIGURE_TERMIOS_PRINTK_CALLOUT tickle_my_watchdog_fnc */ + +#include <termios_printk_cnf.h> diff --git a/cpukit/libmisc/serdbg/serdbg.c b/cpukit/libmisc/serdbg/serdbg.c new file mode 100644 index 0000000000..2f9abf6824 --- /dev/null +++ b/cpukit/libmisc/serdbg/serdbg.c @@ -0,0 +1,92 @@ +/*===============================================================*\ +| Project: RTEMS remote gdb over serial line | ++-----------------------------------------------------------------+ +| File: serdbg.c | ++-----------------------------------------------------------------+ +| Copyright (c) 2002 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file contains intialization and utility functions to add | +| a gdb remote debug stub to an RTEMS system | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 04.04.02 creation doe | +\*===============================================================*/ +/* + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <rtems/serdbg.h> + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int serdbg_init_dbg +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| initialize remote gdb session over serial line | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + void +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| rtems_status_code | +\*=========================================================================*/ +{ + static bool is_initialized = false; + + rtems_status_code rc = RTEMS_SUCCESSFUL; + + if (is_initialized) { + return RTEMS_SUCCESSFUL; + } + is_initialized = true; + /* + * try to open serial device + */ + if (rc == RTEMS_SUCCESSFUL) { + if ((serdbg_conf.open_io != NULL) && + (0 > serdbg_conf.open_io(serdbg_conf.devname,serdbg_conf.baudrate))) { + fprintf(stderr, + "remote_gdb_init: cannot open device %s " + "for gdb connection:%s\n",serdbg_conf.devname,strerror(errno)); + rc = RTEMS_IO_ERROR; + } + } + /* + * initialize gdb stub + */ + if (rc == RTEMS_SUCCESSFUL) { + set_debug_traps(); + } + /* + * now activate gdb stub + */ + if ((rc == RTEMS_SUCCESSFUL) && + !serdbg_conf.skip_init_bkpt) { + breakpoint(); + } + + /* + * return to original function + * this may be already unter gdb control + */ + return rc; +} diff --git a/cpukit/libmisc/serdbg/serdbg.h b/cpukit/libmisc/serdbg/serdbg.h new file mode 100644 index 0000000000..375b03b5d5 --- /dev/null +++ b/cpukit/libmisc/serdbg/serdbg.h @@ -0,0 +1,187 @@ +/*===============================================================*\ +| Project: RTEMS remote gdb over serial line | ++-----------------------------------------------------------------+ +| File: serdbg.h | ++-----------------------------------------------------------------+ +| Copyright (c) 2002 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file declares intialization functions to add | +| a gdb remote debug stub to an RTEMS system | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 04.04.02 creation doe | +\*===============================================================*/ +/* + * $Id$ + */ +#ifndef _SERDBG_H +#define _SERDBG_H + +#include <rtems.h> +#include <termios.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32_t baudrate; /* debug baud rate, e.g. 57600 */ + void (*callout)(void); /* callout pointer during polling */ + int (*open_io)(const char *dev_name, uint32_t baudrate); /* I/O open fnc */ + const char *devname; /* debug device, e.g. "/dev/tty01" */ + bool skip_init_bkpt; /* if TRUE, do not stop when initializing */ +} serdbg_conf_t; + +/* + * must be defined in init module... + */ +extern serdbg_conf_t serdbg_conf; + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +void putDebugChar +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| send character to remote debugger | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + char c /* char to send */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int getDebugChar +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| get character from remote debugger | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + void /* <none> */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +void serdbg_exceptionHandler +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| hook directly to an exception vector | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int vecnum, /* vector index to hook at */ + void *vector /* address of handler function */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int serdbg_init +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| initialize remote gdb session over serial line | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + void + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| rtems_status_code | +\*=========================================================================*/ + +/* + * stuff from serdbgio.c + */ +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int serdbg_open + +/*-------------------------------------------------------------------------*\ +| Purpose: | +| try to open given serial debug port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + const char *dev_name, /* name of device to open */ + uint32_t baudrate /* baud rate to use */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0 on success, -1 and errno otherwise | +\*=========================================================================*/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +void putDebugChar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| send one character to serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + char c /* character to print */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int getDebugChar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| wait for one character from serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + void /* none */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| received character | +\*=========================================================================*/ + +/* + * Assumed to be provided by the BSP + */ +extern void set_debug_traps(void); +extern void breakpoint(void); +#ifdef __cplusplus +} +#endif + +#endif /* _SERDBG_H */ diff --git a/cpukit/libmisc/serdbg/serdbgcnf.h b/cpukit/libmisc/serdbg/serdbgcnf.h new file mode 100644 index 0000000000..3820f61f8a --- /dev/null +++ b/cpukit/libmisc/serdbg/serdbgcnf.h @@ -0,0 +1,89 @@ +/*===============================================================*\ +| Project: RTEMS configure remote gdb over serial line | ++-----------------------------------------------------------------+ +| File: serdbgcnf.h | ++-----------------------------------------------------------------+ +| Copyright (c) 2002 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file declares intialization functions to add | +| a gdb remote debug stub to an RTEMS system | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 13.05.02 creation doe | +\*===============================================================*/ +/* + * $Id$ + */ +#ifndef _SERDBGCNF_H +#define _SERDBGCNF_H + +#include <rtems/serdbg.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef CONFIGURE_INIT + +/* + * fallback for baud rate to use + */ +#ifndef CONFIGURE_SERDBG_BAUDRATE +#define CONFIGURE_SERDBG_BAUDRATE 9600 +#endif + +/* + * fallback for device name to use + */ +#ifndef CONFIGURE_SERDBG_DEVNAME +#define CONFIGURE_SERDBG_DEVNAME "/dev/tty01" +#endif + +/* + * fill in serdbg_conf structure + */ +serdbg_conf_t serdbg_conf = { + CONFIGURE_SERDBG_BAUDRATE, + +#ifdef CONFIGURE_SERDBG_CALLOUT + CONFIGURE_SERDBG_CALLOUT, +#else + NULL, +#endif + +#ifdef CONFIGURE_SERDBG_USE_POLLED_TERMIOS + serdbg_open, +#else + NULL, +#endif + + CONFIGURE_SERDBG_DEVNAME, + +#ifdef CONFIGURE_SERDBG_SKIP_INIT_BKPT + true, +#else + false, +#endif +}; + +int serdbg_init(void) { +#ifdef CONFIGURE_USE_SERDBG + extern int serdbg_init_dbg(void); + return serdbg_init_dbg(); +#else + return 0; +#endif +} + +#endif /* CONFIGURE_INIT */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SERDBGCNF_H */ diff --git a/cpukit/libmisc/serdbg/serdbgio.c b/cpukit/libmisc/serdbg/serdbgio.c new file mode 100644 index 0000000000..6ab5b3c716 --- /dev/null +++ b/cpukit/libmisc/serdbg/serdbgio.c @@ -0,0 +1,256 @@ +/*===============================================================*\ +| File: serdbgio.c | ++-----------------------------------------------------------------+ +| Copyright (c) 2002 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| Hebststr. 8, 82178 Puchheim, Germany | +| <Thomas.Doerfler@imd-systems.de> | +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| http://www.rtems.com/license/LICENSE. | +| all rights reserved | ++-----------------------------------------------------------------+ +| TERMIOS serial gdb interface support | +| the functions in this file allow the standard gdb stubs like | +| "m68k-stub.c" to access any serial interfaces that work with | +| RTEMS termios in polled mode | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 10.05.02 creation doe | +|*****************************************************************| +|* $Id$ + * +|*****************************************************************| +\*===============================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/libio_.h> +#include <errno.h> +#include <unistd.h> /* close */ +#include <stdio.h> +#include <fcntl.h> +#include <termios.h> + +#include <rtems/termiostypes.h> +#include <rtems/serdbg.h> + + +/* + * internal variables + */ +int serdbg_fd = -1; +struct rtems_termios_tty *serdbg_tty; + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int serdbg_open + +/*-------------------------------------------------------------------------*\ +| Purpose: | +| try to open given serial debug port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + const char *dev_name, /* name of device to open */ + uint32_t baudrate /* baud rate to use */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0 on success, -1 and errno otherwise | +\*=========================================================================*/ +{ + bool err_occurred = false; + rtems_libio_t *iop = NULL; + struct termios act_termios; + tcflag_t baudcode = B0; + +#define FD_STORE_CNT 3 + int fd_store[FD_STORE_CNT]; + int fd_store_used = 0; + + /* + * translate baudrate into baud code + */ + switch(baudrate) { + case 50: baudcode = B50; break; + case 75: baudcode = B75; break; + case 110: baudcode = B110; break; + case 134: baudcode = B134; break; + case 150: baudcode = B150; break; + case 200: baudcode = B200; break; + case 300: baudcode = B300; break; + case 600: baudcode = B600; break; + case 1200: baudcode = B1200; break; + case 1800: baudcode = B1800; break; + case 2400: baudcode = B2400; break; + case 4800: baudcode = B4800; break; + case 9600: baudcode = B9600; break; + case 19200: baudcode = B19200; break; + case 38400: baudcode = B38400; break; + case 57600: baudcode = B57600; break; + case 115200: baudcode = B115200; break; + case 230400: baudcode = B230400; break; + case 460800: baudcode = B460800; break; + default : err_occurred = true; errno = EINVAL; break; + } + + /* + * open device for serdbg operation + * skip any fds that are between 0..2, because they are + * reserved for stdin/out/err + */ + if (!err_occurred && + (dev_name != NULL) && + (dev_name[0] != '\0')) { + do { + serdbg_fd = open(dev_name,O_RDWR); + if (serdbg_fd < 0) { + err_occurred = true; + } + else { + if (serdbg_fd < 3) { + if (fd_store_used >= FD_STORE_CNT) { + err_occurred = true; + } + else { + fd_store[fd_store_used++] = serdbg_fd; + } + } + } + } while (!err_occurred && + (serdbg_fd < 3)); + } + /* + * close any fds, that have been placed in fd_store + * so fd 0..2 are reusable again + */ + while (--fd_store_used >= 0) { + close(fd_store[fd_store_used]); + } + + /* + * capture tty structure + */ + if (!err_occurred) { + iop = &rtems_libio_iops[serdbg_fd]; + serdbg_tty = iop->data1; + } + /* + * set device baudrate + * (and transp mode, this is not really needed) + * ... + */ + /* + * ... get fd settings + */ + if (!err_occurred && + (0 != tcgetattr(serdbg_fd,&act_termios))) { + err_occurred = true; + } + if (!err_occurred) { + act_termios.c_iflag + &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + |INLCR|IGNCR|ICRNL|IXON); + act_termios.c_oflag + &= ~OPOST; + + act_termios.c_lflag + &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + + cfsetospeed(&act_termios,baudcode); + cfsetispeed(&act_termios,baudcode); + + if (0 != tcsetattr(serdbg_fd,TCSANOW,&act_termios)) { + err_occurred = true; + } + } + return (err_occurred + ? -1 + : 0); +} + +void putDebugChar(char c) __attribute__ ((__weak__)); +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +void putDebugChar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| send one character to serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + char c /* character to print */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + /* + * call serdbg polling callout, if available + */ + if (serdbg_conf.callout != NULL) { + serdbg_conf.callout(); + } + /* + * check, whether debug serial port is available + */ + if ((serdbg_tty != NULL) && + (serdbg_tty->device.write != NULL)) { + /* + * send character to debug serial port + */ + serdbg_tty->device.write(serdbg_tty->minor,&c,1); + } +} + +int getDebugChar(void) __attribute__ ((__weak__)); +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int getDebugChar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| wait for one character from serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + void /* none */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| received character | +\*=========================================================================*/ +{ + int c = -1; + /* + * check, whether debug serial port is available + */ + if ((serdbg_tty != NULL) && + (serdbg_tty->device.pollRead != NULL)) { + do { + /* + * call serdbg polling callout, if available + */ + if (serdbg_conf.callout != NULL) { + serdbg_conf.callout(); + } + /* + * get character from debug serial port + */ + c = serdbg_tty->device.pollRead(serdbg_tty->minor); + } while (c < 0); + } + return c; +} diff --git a/cpukit/libmisc/serdbg/termios_printk.c b/cpukit/libmisc/serdbg/termios_printk.c new file mode 100644 index 0000000000..bcbfe482fa --- /dev/null +++ b/cpukit/libmisc/serdbg/termios_printk.c @@ -0,0 +1,241 @@ +/*===============================================================*\ +| File: termios_printk.c | ++-----------------------------------------------------------------+ +| Copyright (c) 2002 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| Hebststr. 8, 82178 Puchheim, Germany | +| <Thomas.Doerfler@imd-systems.de> | +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| http://www.rtems.com/license/LICENSE. | +| all rights reserved | ++-----------------------------------------------------------------+ +| TERMIOS printk support | +| this module performs low-level printk output using | +| a polled termios driver | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 13.05.02 creation doe | +|*****************************************************************| +|* $Id$ + * +|*****************************************************************| +\*===============================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/libio_.h> +#include <errno.h> +#include <stdio.h> +#include <fcntl.h> +#include <termios.h> + +#include <rtems/termiostypes.h> +#include <rtems/bspIo.h> +#include <rtems/termios_printk.h> + +/* + * internal variables + */ +int termios_printk_fd = -1; +struct rtems_termios_tty *termios_printk_tty; + +static void _termios_printk_null_char( + char c __attribute__((unused))) +{ + return; +} + +BSP_output_char_function_type BSP_output_char = _termios_printk_null_char; +BSP_polling_getchar_function_type BSP_poll_char; + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +void termios_printk_outputchar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| send one character to serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + char c /* character to print */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + static const char cr = '\r'; + /* + * check, whether printk serial port is available + */ + + if ((termios_printk_tty != NULL) && + (termios_printk_tty->device.write != NULL)) { + /* + * call termios_printk polling callout, if available + */ + if (termios_printk_conf.callout != NULL) { + termios_printk_conf.callout(); + } + /* + * send character to debug serial port + */ + if (c == '\n') { + termios_printk_tty->device.write(termios_printk_tty->minor,&cr,1); + } + termios_printk_tty->device.write(termios_printk_tty->minor,&c,1); + } +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int termios_printk_inputchar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| wait for one character from serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + void /* none */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| received character | +\*=========================================================================*/ +{ + int c = -1; + /* + * check, whether debug serial port is available + */ + if ((termios_printk_tty != NULL) && + (termios_printk_tty->device.pollRead != NULL)) { + do { + /* + * call termios_printk polling callout, if available + */ + if (termios_printk_conf.callout != NULL) { + termios_printk_conf.callout(); + } + /* + * get character from debug serial port + */ + c = termios_printk_tty->device.pollRead(termios_printk_tty->minor); + } while (c < 0); + } + return c; +} + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int termios_printk_open + +/*-------------------------------------------------------------------------*\ +| Purpose: | +| try to open given serial debug port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + const char *dev_name, /* name of device to open */ + uint32_t baudrate /* baud rate to use */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0 on success, -1 and errno otherwise | +\*=========================================================================*/ +{ + bool err_occurred = false; + rtems_libio_t *iop = NULL; + struct termios act_termios; + tcflag_t baudcode = B0; + + if (termios_printk_fd >= 0) { + /* + * already initialized + */ + return 0; + } + /* + * translate baudrate into baud code + */ + switch(baudrate) { + case 50: baudcode = B50; break; + case 75: baudcode = B75; break; + case 110: baudcode = B110; break; + case 134: baudcode = B134; break; + case 150: baudcode = B150; break; + case 200: baudcode = B200; break; + case 300: baudcode = B300; break; + case 600: baudcode = B600; break; + case 1200: baudcode = B1200; break; + case 1800: baudcode = B1800; break; + case 2400: baudcode = B2400; break; + case 4800: baudcode = B4800; break; + case 9600: baudcode = B9600; break; + case 19200: baudcode = B19200; break; + case 38400: baudcode = B38400; break; + case 57600: baudcode = B57600; break; + case 115200: baudcode = B115200; break; + case 230400: baudcode = B230400; break; + case 460800: baudcode = B460800; break; + default : err_occurred = true; errno = EINVAL; break; + } + /* + * open device for serdbg operation + */ + if (!err_occurred && + (dev_name != NULL) && + (dev_name[0] != '\0')) { + termios_printk_fd = open(dev_name,O_RDWR); + if (termios_printk_fd < 0) { + err_occurred = true; + } + } + /* + * capture tty structure + */ + if (!err_occurred) { + iop = &rtems_libio_iops[termios_printk_fd]; + termios_printk_tty = iop->data1; + } + /* + * set device baudrate + * (and transp mode, this is not really needed) + * ... + */ + /* + * ... get fd settings + */ + if (!err_occurred && + (0 != tcgetattr(termios_printk_fd,&act_termios))) { + err_occurred = true; + } + if (!err_occurred) { + + cfsetospeed(&act_termios,baudcode); + cfsetispeed(&act_termios,baudcode); + + if (0 != tcsetattr(termios_printk_fd,TCSANOW,&act_termios)) { + err_occurred = true; + } + } + if (!err_occurred) { + BSP_output_char = termios_printk_outputchar; + BSP_poll_char = termios_printk_inputchar; + } + return (err_occurred + ? -1 + : 0); +} diff --git a/cpukit/libmisc/serdbg/termios_printk.h b/cpukit/libmisc/serdbg/termios_printk.h new file mode 100644 index 0000000000..4f484df62f --- /dev/null +++ b/cpukit/libmisc/serdbg/termios_printk.h @@ -0,0 +1,104 @@ +/*===============================================================*\ +| Project: RTEMS remote gdb over serial line | ++-----------------------------------------------------------------+ +| File: termios_printk.h | ++-----------------------------------------------------------------+ +| Copyright (c) 2002 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file declares intialization functions to add | +| printk polled output via termios polled drivers | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 13.04.02 creation doe | +\*===============================================================*/ +/* + * $Id$ + */ +#ifndef _TERMIOS_PRINTK_H +#define _TERMIOS_PRINTK_H + +#include <rtems.h> +#include <termios.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32_t baudrate; /* debug baud rate, e.g. 57600 */ + void (*callout)(void); /* callout pointer during polling */ + const char *devname; /* debug device, e.g. "/dev/tty01" */ +} termios_printk_conf_t; + +/* + * must be defined in init module... + */ +extern termios_printk_conf_t termios_printk_conf; + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +void termios_printk_outputchar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| send one character to serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + char c /* character to print */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int termios_printk_inputchar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| wait for one character from serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + void /* none */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| received character | +\*=========================================================================*/ + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int termios_printk_open + +/*-------------------------------------------------------------------------*\ +| Purpose: | +| try to open given serial debug port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + const char *dev_name, /* name of device to open */ + uint32_t baudrate /* baud rate to use */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0 on success, -1 and errno otherwise | +\*=========================================================================*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _TERMIOS_PRINTK_H */ diff --git a/cpukit/libmisc/serdbg/termios_printk_cnf.h b/cpukit/libmisc/serdbg/termios_printk_cnf.h new file mode 100644 index 0000000000..2e8a7ac043 --- /dev/null +++ b/cpukit/libmisc/serdbg/termios_printk_cnf.h @@ -0,0 +1,78 @@ +/*===============================================================*\ +| Project: RTEMS configure remote gdb over serial line | ++-----------------------------------------------------------------+ +| File: termios_printk_cnf.h | ++-----------------------------------------------------------------+ +| Copyright (c) 2002 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file declares intialization functions to add | +| printk support via polled termios | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 13.05.02 creation doe | +\*===============================================================*/ +/* + * $Id$ + */ +#ifndef _TERMIOS_PRINTK_CNF_H +#define _TERMIOS_PRINTK_CNF_H + +#include <rtems/termios_printk.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef CONFIGURE_INIT + +/* + * fallback for baud rate to use + */ +#ifndef CONFIGURE_TERMIOS_PRINTK_BAUDRATE +#define CONFIGURE_TERMIOS_PRINTK_BAUDRATE 9600 +#endif + +/* + * fallback for device name to use + */ +#ifndef CONFIGURE_TERMIOS_PRINTK_DEVNAME +#define CONFIGURE_TERMIOS_PRINTK_DEVNAME "/dev/console" +#endif + +#ifdef CONFIGURE_USE_TERMIOS_PRINTK +/* + * fill in termios_printk_conf structure + */ +termios_printk_conf_t termios_printk_conf = { + CONFIGURE_TERMIOS_PRINTK_BAUDRATE, + +#ifdef CONFIGURE_TERMIOS_PRINTK_CALLOUT + CONFIGURE_TERMIOS_PRINTK_CALLOUT, +#else + NULL, +#endif + CONFIGURE_TERMIOS_PRINTK_DEVNAME, +}; +#endif + +int termios_printk_init(void) { +#ifdef CONFIGURE_USE_TERMIOS_PRINTK + return termios_printk_open(termios_printk_conf.devname, + termios_printk_conf.baudrate); +#else + return 0; +#endif +} + +#endif /* CONFIGURE_INIT */ + +#ifdef __cplusplus +} +#endif + +#endif /* _TERMIOS_PRINTK_CNF_H */ diff --git a/cpukit/libmisc/shell/README b/cpukit/libmisc/shell/README new file mode 100644 index 0000000000..9798becc37 --- /dev/null +++ b/cpukit/libmisc/shell/README @@ -0,0 +1,27 @@ +# +# $Id$ +# + +This directory contains a shell user extension +primary features: + + + create a user shell terminal task. + +This code has not been extensively tested. It is provided as a tool +for RTEMS users to open more shell terminal. +Suggestions and comments are appreciated. + +NOTES: + +1. printf() & getchar() works but you can't use + 0,1,2 like fildes. You need to use fileno(stdin),fileno(stdout),... + +2. You only need a termios dev to start a new session, add your new commands + and enjoy it. + +3. Telnetd daemon uses this (browse libnetworking/rtems_telnetd) + Enjoy it. + +FUTURE: + +1. Adding new commands in cmds.c to give file manegement to shell. diff --git a/cpukit/libmisc/shell/cat_file.c b/cpukit/libmisc/shell/cat_file.c new file mode 100644 index 0000000000..1a5efb4418 --- /dev/null +++ b/cpukit/libmisc/shell/cat_file.c @@ -0,0 +1,37 @@ +/* + * CAT Command Implementation + * + * Author: + * WORK: fernando.ruiz@ctv.es + * HOME: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> + +int rtems_shell_cat_file(FILE * out,const char * name) { + FILE * fd; + int c; + + if (out) { + fd = fopen(name,"r"); + if (!fd) { + return -1; + } + while ((c=fgetc(fd))!=EOF) + fputc(c,out); + fclose(fd); + } + return 0; +} + + diff --git a/cpukit/libmisc/shell/cmds.c b/cpukit/libmisc/shell/cmds.c new file mode 100644 index 0000000000..e8d6c581df --- /dev/null +++ b/cpukit/libmisc/shell/cmds.c @@ -0,0 +1,77 @@ +/* + * XXX -- Just monitor commands until those can be integrated better + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <rtems.h> +#include <rtems/monitor.h> +#include <rtems/shell.h> +#include "internal.h" + +/*-----------------------------------------------------------* + * with this you can call at all the rtems monitor commands. + * Not all work fine but you can show the rtems status and more. + *-----------------------------------------------------------*/ +int rtems_shell_main_monitor(int argc, char **argv) { + const rtems_monitor_command_entry_t *command = NULL; + + if (argc < 1) { + return 1; + } + + command = rtems_monitor_command_lookup(argv [0]); + + if (command == NULL) { + return 1; + } + + command->command_function(argc, argv, &command->command_arg, 0); + + return 0; +} + +static bool rtems_shell_register_command(const rtems_monitor_command_entry_t *e, void *arg __attribute__((unused))) +{ + /* Exclude EXIT (alias quit)*/ + if (strcmp("exit", e->command) != 0) { + rtems_shell_cmd_t *shell_cmd = + (rtems_shell_cmd_t *) malloc(sizeof(rtems_shell_cmd_t)); + + if (shell_cmd != NULL) { + shell_cmd->name = e->command; + shell_cmd->topic = "monitor"; + shell_cmd->usage = e->usage; + shell_cmd->command = rtems_shell_main_monitor; + shell_cmd->alias = NULL; + shell_cmd->next = NULL; + + if (rtems_shell_add_cmd_struct(shell_cmd) == NULL) { + free(shell_cmd); + } + } + } + + return true; +} + +void rtems_shell_register_monitor_commands(void) +{ + rtems_monitor_command_iterate(rtems_shell_register_command, NULL); +} diff --git a/cpukit/libmisc/shell/cmp-ls.c b/cpukit/libmisc/shell/cmp-ls.c new file mode 100644 index 0000000000..793e607fe1 --- /dev/null +++ b/cpukit/libmisc/shell/cmp-ls.c @@ -0,0 +1,204 @@ +/* $NetBSD: cmp.c,v 1.17 2003/08/07 09:05:14 agc Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if 0 +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cmp.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: cmp.c,v 1.17 2003/08/07 09:05:14 agc Exp $"); +#endif +#endif /* not lint */ +#endif + +#include <sys/types.h> +#include <sys/stat.h> + +#include <fts.h> +#include <string.h> + +#include "extern-ls.h" + +#if defined(__rtems__) || defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) || \ + defined(_XOPEN_SOURCE) || defined(__NetBSD__) +#define ATIMENSEC_CMP(x, op, y) ((x)->st_atime op (y)->st_atime) +#define CTIMENSEC_CMP(x, op, y) ((x)->st_ctime op (y)->st_ctime) +#define MTIMENSEC_CMP(x, op, y) ((x)->st_mtime op (y)->st_mtime) +#else +#define ATIMENSEC_CMP(x, op, y) \ + ((x)->st_atime.tv_nsec op (y)->st_atime.tv_nsec) +#define CTIMENSEC_CMP(x, op, y) \ + ((x)->st_ctime.tv_nsec op (y)->st_ctime.tv_nsec) +#define MTIMENSEC_CMP(x, op, y) \ + ((x)->st_mtime.tv_nsec op (y)->st_mtime.tv_nsec) +#endif + +int +namecmp(const FTSENT *a, const FTSENT *b) +{ + + return (strcmp(a->fts_name, b->fts_name)); +} + +int +revnamecmp(const FTSENT *a, const FTSENT *b) +{ + + return (strcmp(b->fts_name, a->fts_name)); +} + +int +modcmp(const FTSENT *a, const FTSENT *b) +{ + + if (b->fts_statp->st_mtime > a->fts_statp->st_mtime) + return (1); + else if (b->fts_statp->st_mtime < a->fts_statp->st_mtime) + return (-1); + else if (MTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (1); + else if (MTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (-1); + else + return (namecmp(a, b)); +} + +int +revmodcmp(const FTSENT *a, const FTSENT *b) +{ + + if (b->fts_statp->st_mtime > a->fts_statp->st_mtime) + return (-1); + else if (b->fts_statp->st_mtime < a->fts_statp->st_mtime) + return (1); + else if (MTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (-1); + else if (MTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (1); + else + return (revnamecmp(a, b)); +} + +int +acccmp(const FTSENT *a, const FTSENT *b) +{ + + if (b->fts_statp->st_atime > a->fts_statp->st_atime) + return (1); + else if (b->fts_statp->st_atime < a->fts_statp->st_atime) + return (-1); + else if (ATIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (1); + else if (ATIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (-1); + else + return (namecmp(a, b)); +} + +int +revacccmp(const FTSENT *a, const FTSENT *b) +{ + + if (b->fts_statp->st_atime > a->fts_statp->st_atime) + return (-1); + else if (b->fts_statp->st_atime < a->fts_statp->st_atime) + return (1); + else if (ATIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (-1); + else if (ATIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (1); + else + return (revnamecmp(a, b)); +} + +int +statcmp(const FTSENT *a, const FTSENT *b) +{ + + if (b->fts_statp->st_ctime > a->fts_statp->st_ctime) + return (1); + else if (b->fts_statp->st_ctime < a->fts_statp->st_ctime) + return (-1); + else if (CTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (1); + else if (CTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (-1); + else + return (namecmp(a, b)); +} + +int +revstatcmp(const FTSENT *a, const FTSENT *b) +{ + + if (b->fts_statp->st_ctime > a->fts_statp->st_ctime) + return (-1); + else if (b->fts_statp->st_ctime < a->fts_statp->st_ctime) + return (1); + else if (CTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (-1); + else if (CTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (1); + else + return (revnamecmp(a, b)); +} + +int +sizecmp(const FTSENT *a, const FTSENT *b) +{ + + if (b->fts_statp->st_size > a->fts_statp->st_size) + return (1); + if (b->fts_statp->st_size < a->fts_statp->st_size) + return (-1); + else + return (namecmp(a, b)); +} + +int +revsizecmp(const FTSENT *a, const FTSENT *b) +{ + + if (b->fts_statp->st_size > a->fts_statp->st_size) + return (-1); + if (b->fts_statp->st_size < a->fts_statp->st_size) + return (1); + else + return (revnamecmp(a, b)); +} diff --git a/cpukit/libmisc/shell/dd-args.c b/cpukit/libmisc/shell/dd-args.c new file mode 100644 index 0000000000..a271287166 --- /dev/null +++ b/cpukit/libmisc/shell/dd-args.c @@ -0,0 +1,507 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)args.c 8.3 (Berkeley) 4/2/94"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/bin/dd/args.c,v 1.40 2004/08/15 19:10:05 rwatson Exp $"); + +#include <sys/types.h> + +#include <err.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#include "dd.h" +#include "extern-dd.h" + +#define strtouq strtoul +#define strtoq strtol + +static int c_arg(const void *, const void *); +static int c_conv(const void *, const void *); +static void f_bs(rtems_shell_dd_globals* globals, char *); +static void f_cbs(rtems_shell_dd_globals* globals, char *); +static void f_conv(rtems_shell_dd_globals* globals, char *); +static void f_count(rtems_shell_dd_globals* globals, char *); +static void f_files(rtems_shell_dd_globals* globals, char *); +static void f_fillchar(rtems_shell_dd_globals* globals, char *); +static void f_ibs(rtems_shell_dd_globals* globals, char *); +static void f_if(rtems_shell_dd_globals* globals, char *); +static void f_obs(rtems_shell_dd_globals* globals, char *); +static void f_of(rtems_shell_dd_globals* globals, char *); +static void f_seek(rtems_shell_dd_globals* globals, char *); +static void f_skip(rtems_shell_dd_globals* globals, char *); +static uintmax_t get_num(rtems_shell_dd_globals* globals, const char *); +static off_t get_off_t(rtems_shell_dd_globals* globals, const char *); + +static const struct arg { + const char *name; + void (*f)(rtems_shell_dd_globals* globals, char *); + uint_least32_t set, noset; +} args[] = { + { "bs", f_bs, C_BS, C_BS|C_IBS|C_OBS|C_OSYNC }, + { "cbs", f_cbs, C_CBS, C_CBS }, + { "conv", f_conv, 0, 0 }, + { "count", f_count, C_COUNT, C_COUNT }, + { "files", f_files, C_FILES, C_FILES }, + { "fillchar", f_fillchar, C_FILL, C_FILL }, + { "ibs", f_ibs, C_IBS, C_BS|C_IBS }, + { "if", f_if, C_IF, C_IF }, + { "iseek", f_skip, C_SKIP, C_SKIP }, + { "obs", f_obs, C_OBS, C_BS|C_OBS }, + { "of", f_of, C_OF, C_OF }, + { "oseek", f_seek, C_SEEK, C_SEEK }, + { "seek", f_seek, C_SEEK, C_SEEK }, + { "skip", f_skip, C_SKIP, C_SKIP }, +}; + +static char *oper; + +/* + * args -- parse JCL syntax of dd. + */ +void +jcl(rtems_shell_dd_globals* globals, char **argv) +{ + struct arg *ap, tmp; + char *arg; + + oper = NULL; + + in.dbsz = out.dbsz = 512; + + while ((oper = *++argv) != NULL) { +// if ((oper = strdup(oper)) == NULL) +// errx(exit_jump, 1, "unable to allocate space for the argument \"%s\"", *argv); + if ((arg = strchr(oper, '=')) == NULL) + errx(exit_jump, 1, "unknown operand %s", oper); + *arg++ = '\0'; + if (!*arg) + errx(exit_jump, 1, "no value specified for %s", oper); + tmp.name = oper; + if (!(ap = (struct arg *)bsearch(&tmp, args, + sizeof(args)/sizeof(struct arg), sizeof(struct arg), + c_arg))) + errx(exit_jump, 1, "unknown operand %s", tmp.name); + if (ddflags & ap->noset) + errx(exit_jump, 1, "%s: illegal argument combination or already set", + tmp.name); + ddflags |= ap->set; + ap->f(globals, arg); + } + + /* Final sanity checks. */ + + if (ddflags & C_BS) { + /* + * Bs is turned off by any conversion -- we assume the user + * just wanted to set both the input and output block sizes + * and didn't want the bs semantics, so we don't warn. + */ + if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE | + C_UNBLOCK)) + ddflags &= ~C_BS; + + /* Bs supersedes ibs and obs. */ + if (ddflags & C_BS && ddflags & (C_IBS | C_OBS)) + warnx("bs supersedes ibs and obs"); + } + + /* + * Ascii/ebcdic and cbs implies block/unblock. + * Block/unblock requires cbs and vice-versa. + */ + if (ddflags & (C_BLOCK | C_UNBLOCK)) { + if (!(ddflags & C_CBS)) + errx(exit_jump, 1, "record operations require cbs"); + if (cbsz == 0) + errx(exit_jump, 1, "cbs cannot be zero"); + cfunc = ddflags & C_BLOCK ? block : unblock; + } else if (ddflags & C_CBS) { + if (ddflags & (C_ASCII | C_EBCDIC)) { + if (ddflags & C_ASCII) { + ddflags |= C_UNBLOCK; + cfunc = unblock; + } else { + ddflags |= C_BLOCK; + cfunc = block; + } + } else + errx(exit_jump, 1, "cbs meaningless if not doing record operations"); + } else + cfunc = def; + + /* + * Bail out if the calculation of a file offset would overflow. + */ + if (in.offset > OFF_MAX / (ssize_t)in.dbsz || + out.offset > OFF_MAX / (ssize_t)out.dbsz) + errx(exit_jump, 1, "seek offsets cannot be larger than %jd", + (intmax_t)OFF_MAX); +} + +static int +c_arg(const void *a, const void *b) +{ + + return (strcmp(((const struct arg *)a)->name, + ((const struct arg *)b)->name)); +} + +static void +f_bs(rtems_shell_dd_globals* globals, char *arg) +{ + uintmax_t res; + + res = get_num(globals, arg); + if (res < 1 || res > SSIZE_MAX) + errx(exit_jump, 1, "bs must be between 1 and %jd", (intmax_t)SSIZE_MAX); + in.dbsz = out.dbsz = (size_t)res; +} + +static void +f_cbs(rtems_shell_dd_globals* globals, char *arg) +{ + uintmax_t res; + + res = get_num(globals, arg); + if (res < 1 || res > SSIZE_MAX) + errx(exit_jump, 1, "cbs must be between 1 and %jd", (intmax_t)SSIZE_MAX); + cbsz = (size_t)res; +} + +static void +f_count(rtems_shell_dd_globals* globals, char *arg) +{ + intmax_t res; + + res = (intmax_t)get_num(globals, arg); + if (res < 0) + errx(exit_jump, 1, "count cannot be negative"); + if (res == 0) + cpy_cnt = (uintmax_t)-1; + else + cpy_cnt = (uintmax_t)res; +} + +static void +f_files(rtems_shell_dd_globals* globals, char *arg) +{ + + files_cnt = get_num(globals, arg); + if (files_cnt < 1) + errx(exit_jump, 1, "files must be between 1 and %jd", (uintmax_t)-1); +} + +static void +f_fillchar(rtems_shell_dd_globals* globals, char *arg) +{ + + if (strlen(arg) != 1) + errx(exit_jump, 1, "need exactly one fill char"); + + fill_char = arg[0]; +} + +static void +f_ibs(rtems_shell_dd_globals* globals, char *arg) +{ + uintmax_t res; + + if (!(ddflags & C_BS)) { + res = get_num(globals, arg); + if (res < 1 || res > SSIZE_MAX) + errx(exit_jump, 1, "ibs must be between 1 and %jd", + (intmax_t)SSIZE_MAX); + in.dbsz = (size_t)res; + } +} + +static void +f_if(rtems_shell_dd_globals* globals, char *arg) +{ + + in.name = strdup(arg); +} + +static void +f_obs(rtems_shell_dd_globals* globals, char *arg) +{ + uintmax_t res; + + if (!(ddflags & C_BS)) { + res = get_num(globals, arg); + if (res < 1 || res > SSIZE_MAX) + errx(exit_jump, 1, "obs must be between 1 and %jd", + (intmax_t)SSIZE_MAX); + out.dbsz = (size_t)res; + } +} + +static void +f_of(rtems_shell_dd_globals* globals, char *arg) +{ + + out.name = strdup(arg); +} + +static void +f_seek(rtems_shell_dd_globals* globals, char *arg) +{ + + out.offset = get_off_t(globals, arg); +} + +static void +f_skip(rtems_shell_dd_globals* globals, char *arg) +{ + + in.offset = get_off_t(globals, arg); +} + +static const struct conv { + const char *name; + uint_least32_t set, noset; + const u_char *ctab_; +} clist[] = { + { "ascii", C_ASCII, C_EBCDIC, e2a_POSIX }, + { "block", C_BLOCK, C_UNBLOCK, NULL }, + { "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX }, + { "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX }, + { "lcase", C_LCASE, C_UCASE, NULL }, + { "noerror", C_NOERROR, 0, NULL }, + { "notrunc", C_NOTRUNC, 0, NULL }, + { "oldascii", C_ASCII, C_EBCDIC, e2a_32V }, + { "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V }, + { "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V }, + { "osync", C_OSYNC, C_BS, NULL }, + { "pareven", C_PAREVEN, C_PARODD|C_PARSET|C_PARNONE, NULL}, + { "parnone", C_PARNONE, C_PARODD|C_PARSET|C_PAREVEN, NULL}, + { "parodd", C_PARODD, C_PAREVEN|C_PARSET|C_PARNONE, NULL}, + { "parset", C_PARSET, C_PARODD|C_PAREVEN|C_PARNONE, NULL}, + { "sparse", C_SPARSE, 0, NULL }, + { "swab", C_SWAB, 0, NULL }, + { "sync", C_SYNC, 0, NULL }, + { "ucase", C_UCASE, C_LCASE, NULL }, + { "unblock", C_UNBLOCK, C_BLOCK, NULL }, +}; + +static void +f_conv(rtems_shell_dd_globals* globals, char *arg) +{ + struct conv *cp, tmp; + + while (arg != NULL) { + tmp.name = strsep(&arg, ","); + cp = bsearch(&tmp, clist, sizeof(clist) / sizeof(struct conv), + sizeof(struct conv), c_conv); + if (cp == NULL) + errx(exit_jump, 1, "unknown conversion %s", tmp.name); + if (ddflags & cp->noset) + errx(exit_jump, 1, "%s: illegal conversion combination", tmp.name); + ddflags |= cp->set; + if (cp->ctab_) + ctab = cp->ctab_; + } +} + +static int +c_conv(const void *a, const void *b) +{ + + return (strcmp(((const struct conv *)a)->name, + ((const struct conv *)b)->name)); +} + +/* + * Convert an expression of the following forms to a uintmax_t. + * 1) A positive decimal number. + * 2) A positive decimal number followed by a 'b' or 'B' (mult by 512). + * 3) A positive decimal number followed by a 'k' or 'K' (mult by 1 << 10). + * 4) A positive decimal number followed by a 'm' or 'M' (mult by 1 << 20). + * 5) A positive decimal number followed by a 'g' or 'G' (mult by 1 << 30). + * 5) A positive decimal number followed by a 'w' or 'W' (mult by sizeof int). + * 6) Two or more positive decimal numbers (with/without [BbKkMmGgWw]) + * separated by 'x' or 'X' (also '*' for backwards compatibility), + * specifying the product of the indicated values. + */ +static uintmax_t +get_num(rtems_shell_dd_globals* globals, const char *val) +{ + uintmax_t num, mult, prevnum; + char *expr; + + errno = 0; + num = strtouq(val, &expr, 0); + if (errno != 0) /* Overflow or underflow. */ + err(exit_jump, 1, "%s", oper); + + if (expr == val) /* No valid digits. */ + errx(exit_jump, 1, "%s: illegal numeric value", oper); + + mult = 0; + switch (*expr) { + case 'B': + case 'b': + mult = UINT32_C(512); + break; + case 'K': + case 'k': + mult = UINT32_C(1) << 10; + break; + case 'M': + case 'm': + mult = UINT32_C(1) << 20; + break; + case 'G': + case 'g': + mult = UINT32_C(1) << 30; + break; + case 'W': + case 'w': + mult = sizeof(int); + break; + default: + ; + } + + if (mult != 0) { + prevnum = num; + num *= mult; + /* Check for overflow. */ + if (num / mult != prevnum) + goto erange; + expr++; + } + + switch (*expr) { + case '\0': + break; + case '*': /* Backward compatible. */ + case 'X': + case 'x': + mult = get_num(globals, expr + 1); + prevnum = num; + num *= mult; + if (num / mult == prevnum) + break; +erange: + errx(exit_jump, 1, "%s: %s", oper, strerror(ERANGE)); + default: + errx(exit_jump, 1, "%s: illegal numeric value", oper); + } + return (num); +} + +/* + * Convert an expression of the following forms to an off_t. This is the + * same as get_num(), but it uses signed numbers. + * + * The major problem here is that an off_t may not necessarily be a intmax_t. + */ +static off_t +get_off_t(rtems_shell_dd_globals* globals, const char *val) +{ + intmax_t num, mult, prevnum; + char *expr; + + errno = 0; + num = strtoq(val, &expr, 0); + if (errno != 0) /* Overflow or underflow. */ + err(exit_jump, 1, "%s", oper); + + if (expr == val) /* No valid digits. */ + errx(exit_jump, 1, "%s: illegal numeric value", oper); + + mult = 0; + switch (*expr) { + case 'B': + case 'b': + mult = UINT32_C(512); + break; + case 'K': + case 'k': + mult = UINT32_C(1) << 10; + break; + case 'M': + case 'm': + mult = UINT32_C(1) << 20; + break; + case 'G': + case 'g': + mult = UINT32_C(1) << 30; + break; + case 'W': + case 'w': + mult = sizeof(int); + break; + } + + if (mult != 0) { + prevnum = num; + num *= mult; + /* Check for overflow. */ + if ((prevnum > 0) != (num > 0) || num / mult != prevnum) + goto erange; + expr++; + } + + switch (*expr) { + case '\0': + break; + case '*': /* Backward compatible. */ + case 'X': + case 'x': + mult = (intmax_t)get_off_t(globals, expr + 1); + prevnum = num; + num *= mult; + if ((prevnum > 0) == (num > 0) && num / mult == prevnum) + break; +erange: + errx(exit_jump, 1, "%s: %s", oper, strerror(ERANGE)); + default: + errx(exit_jump, 1, "%s: illegal numeric value", oper); + } + return (num); +} diff --git a/cpukit/libmisc/shell/dd-conv.c b/cpukit/libmisc/shell/dd-conv.c new file mode 100644 index 0000000000..81dba4798b --- /dev/null +++ b/cpukit/libmisc/shell/dd-conv.c @@ -0,0 +1,272 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)conv.c 8.3 (Berkeley) 4/2/94"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/bin/dd/conv.c,v 1.19 2004/04/06 20:06:45 markm Exp $"); + +#include <sys/param.h> + +#include <err.h> +#include <inttypes.h> +#include <string.h> + +#include "dd.h" +#include "extern-dd.h" + +/* + * def -- + * Copy input to output. Input is buffered until reaches obs, and then + * output until less than obs remains. Only a single buffer is used. + * Worst case buffer calculation is (ibs + obs - 1). + */ +void +def(rtems_shell_dd_globals* globals) +{ + u_char *inp; + const u_char *t; + size_t cnt; + + if ((t = ctab) != NULL) + for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp) + *inp = t[*inp]; + + /* Make the output buffer look right. */ + out.dbp = in.dbp; + out.dbcnt = in.dbcnt; + + if (in.dbcnt >= out.dbsz) { + /* If the output buffer is full, write it. */ + dd_out(globals, 0); + + /* + * Ddout copies the leftover output to the beginning of + * the buffer and resets the output buffer. Reset the + * input buffer to match it. + */ + in.dbp = out.dbp; + in.dbcnt = out.dbcnt; + } +} + +void +def_close(rtems_shell_dd_globals* globals) +{ + /* Just update the count, everything is already in the buffer. */ + if (in.dbcnt) + out.dbcnt = in.dbcnt; +} + +/* + * Copy variable length newline terminated records with a max size cbsz + * bytes to output. Records less than cbs are padded with spaces. + * + * max in buffer: MAX(ibs, cbsz) + * max out buffer: obs + cbsz + */ +void +block(rtems_shell_dd_globals* globals) +{ + u_char *inp, *outp; + const u_char *t; + size_t cnt, maxlen; + static int intrunc; + int ch; + + /* + * Record truncation can cross block boundaries. If currently in a + * truncation state, keep tossing characters until reach a newline. + * Start at the beginning of the buffer, as the input buffer is always + * left empty. + */ + if (intrunc) { + for (inp = in.db, cnt = in.dbrcnt; cnt && *inp++ != '\n'; --cnt) + ; + if (!cnt) { + in.dbcnt = 0; + in.dbp = in.db; + return; + } + intrunc = 0; + /* Adjust the input buffer numbers. */ + in.dbcnt = cnt - 1; + in.dbp = inp + cnt - 1; + } + + /* + * Copy records (max cbsz size chunks) into the output buffer. The + * translation is done as we copy into the output buffer. + */ + ch = 0; + for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) { + maxlen = MIN(cbsz, in.dbcnt); + if ((t = ctab) != NULL) + for (cnt = 0; cnt < maxlen && (ch = *inp++) != '\n'; + ++cnt) + *outp++ = t[ch]; + else + for (cnt = 0; cnt < maxlen && (ch = *inp++) != '\n'; + ++cnt) + *outp++ = ch; + /* + * Check for short record without a newline. Reassemble the + * input block. + */ + if (ch != '\n' && in.dbcnt < cbsz) { + (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); + break; + } + + /* Adjust the input buffer numbers. */ + in.dbcnt -= cnt; + if (ch == '\n') + --in.dbcnt; + + /* Pad short records with spaces. */ + if (cnt < cbsz) + (void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt); + else { + /* + * If the next character wouldn't have ended the + * block, it's a truncation. + */ + if (!in.dbcnt || *inp != '\n') + ++st.trunc; + + /* Toss characters to a newline. */ + for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt) + ; + if (!in.dbcnt) + intrunc = 1; + else + --in.dbcnt; + } + + /* Adjust output buffer numbers. */ + out.dbp += cbsz; + if ((out.dbcnt += cbsz) >= out.dbsz) + dd_out(globals, 0); + outp = out.dbp; + } + in.dbp = in.db + in.dbcnt; +} + +void +block_close(rtems_shell_dd_globals* globals) +{ + /* + * Copy any remaining data into the output buffer and pad to a record. + * Don't worry about truncation or translation, the input buffer is + * always empty when truncating, and no characters have been added for + * translation. The bottom line is that anything left in the input + * buffer is a truncated record. Anything left in the output buffer + * just wasn't big enough. + */ + if (in.dbcnt) { + ++st.trunc; + (void)memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt); + (void)memset(out.dbp + in.dbcnt, ctab ? ctab[' '] : ' ', + cbsz - in.dbcnt); + out.dbcnt += cbsz; + } +} + +/* + * Convert fixed length (cbsz) records to variable length. Deletes any + * trailing blanks and appends a newline. + * + * max in buffer: MAX(ibs, cbsz) + cbsz + * max out buffer: obs + cbsz + */ +void +unblock(rtems_shell_dd_globals* globals) +{ + u_char *inp; + const u_char *t; + size_t cnt; + + /* Translation and case conversion. */ + if ((t = ctab) != NULL) + for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp) + *inp = t[*inp]; + /* + * Copy records (max cbsz size chunks) into the output buffer. The + * translation has to already be done or we might not recognize the + * spaces. + */ + for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) { + for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t) + ; + if (t >= inp) { + cnt = t - inp + 1; + (void)memmove(out.dbp, inp, cnt); + out.dbp += cnt; + out.dbcnt += cnt; + } + *out.dbp++ = '\n'; + if (++out.dbcnt >= out.dbsz) + dd_out(globals, 0); + } + if (in.dbcnt) + (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); + in.dbp = in.db + in.dbcnt; +} + +void +unblock_close(rtems_shell_dd_globals* globals) +{ + u_char *t; + size_t cnt; + + if (in.dbcnt) { + warnx("%s: short input record", in.name); + for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t) + ; + if (t >= in.db) { + cnt = t - in.db + 1; + (void)memmove(out.dbp, in.db, cnt); + out.dbp += cnt; + out.dbcnt += cnt; + } + ++out.dbcnt; + *out.dbp++ = '\n'; + } +} diff --git a/cpukit/libmisc/shell/dd-conv_tab.c b/cpukit/libmisc/shell/dd-conv_tab.c new file mode 100644 index 0000000000..782ed97be1 --- /dev/null +++ b/cpukit/libmisc/shell/dd-conv_tab.c @@ -0,0 +1,288 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)conv_tab.c 8.1 (Berkeley) 5/31/93"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/bin/dd/conv_tab.c,v 1.12 2004/04/06 20:06:45 markm Exp $"); + +#include <sys/types.h> + +/* + * There are currently six tables: + * + * ebcdic -> ascii 32V conv=oldascii + * ascii -> ebcdic 32V conv=oldebcdic + * ascii -> ibm ebcdic 32V conv=oldibm + * + * ebcdic -> ascii POSIX/S5 conv=ascii + * ascii -> ebcdic POSIX/S5 conv=ebcdic + * ascii -> ibm ebcdic POSIX/S5 conv=ibm + * + * Other tables are built from these if multiple conversions are being + * done. + * + * Tables used for conversions to/from IBM and EBCDIC to support an extension + * to POSIX P1003.2/D11. The tables referencing POSIX contain data extracted + * from tables 4-3 and 4-4 in P1003.2/Draft 11. The historic tables were + * constructed by running against a file with all possible byte values. + * + * More information can be obtained in "Correspondences of 8-Bit and Hollerith + * Codes for Computer Environments-A USASI Tutorial", Communications of the + * ACM, Volume 11, Number 11, November 1968, pp. 783-789. + */ + +u_char casetab[256]; + +/* EBCDIC to ASCII -- 32V compatible. */ +const u_char e2a_32V[] = { + 0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177, /* 0000 */ + 0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207, /* 0020 */ + 0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037, /* 0030 */ + 0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033, /* 0040 */ + 0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007, /* 0050 */ + 0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004, /* 0060 */ + 0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032, /* 0070 */ + 0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, /* 0100 */ + 0247, 0250, 0133, 0056, 0074, 0050, 0053, 0041, /* 0110 */ + 0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0120 */ + 0260, 0261, 0135, 0044, 0052, 0051, 0073, 0136, /* 0130 */ + 0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267, /* 0140 */ + 0270, 0271, 0174, 0054, 0045, 0137, 0076, 0077, /* 0150 */ + 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, /* 0160 */ + 0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042, /* 0170 */ + 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0200 */ + 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311, /* 0210 */ + 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160, /* 0220 */ + 0161, 0162, 0313, 0314, 0315, 0316, 0317, 0320, /* 0230 */ + 0321, 0176, 0163, 0164, 0165, 0166, 0167, 0170, /* 0240 */ + 0171, 0172, 0322, 0323, 0324, 0325, 0326, 0327, /* 0250 */ + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0260 */ + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, /* 0270 */ + 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0300 */ + 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355, /* 0310 */ + 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120, /* 0320 */ + 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363, /* 0330 */ + 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130, /* 0340 */ + 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371, /* 0350 */ + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0360 */ + 0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to EBCDIC -- 32V compatible. */ +const u_char a2e_32V[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0117, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0112, 0340, 0132, 0137, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0152, 0320, 0241, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to IBM EBCDIC -- 32V compatible. */ +const u_char a2ibm_32V[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0117, 0320, 0241, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* EBCDIC to ASCII -- POSIX and System V compatible. */ +const u_char e2a_POSIX[] = { + 0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177, /* 0000 */ + 0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207, /* 0020 */ + 0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037, /* 0030 */ + 0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033, /* 0040 */ + 0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007, /* 0050 */ + 0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004, /* 0060 */ + 0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032, /* 0070 */ + 0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, /* 0100 */ + 0247, 0250, 0325, 0056, 0074, 0050, 0053, 0174, /* 0110 */ + 0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0120 */ + 0260, 0261, 0041, 0044, 0052, 0051, 0073, 0176, /* 0130 */ + 0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267, /* 0140 */ + 0270, 0271, 0313, 0054, 0045, 0137, 0076, 0077, /* 0150 */ + 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, /* 0160 */ + 0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042, /* 0170 */ + 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0200 */ + 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311, /* 0210 */ + 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160, /* 0220 */ + 0161, 0162, 0136, 0314, 0315, 0316, 0317, 0320, /* 0230 */ + 0321, 0345, 0163, 0164, 0165, 0166, 0167, 0170, /* 0240 */ + 0171, 0172, 0322, 0323, 0324, 0133, 0326, 0327, /* 0250 */ + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0260 */ + 0340, 0341, 0342, 0343, 0344, 0135, 0346, 0347, /* 0270 */ + 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0300 */ + 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355, /* 0310 */ + 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120, /* 0320 */ + 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363, /* 0330 */ + 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130, /* 0340 */ + 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371, /* 0350 */ + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0360 */ + 0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to EBCDIC -- POSIX and System V compatible. */ +const u_char a2e_POSIX[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0255, 0340, 0275, 0232, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0117, 0320, 0137, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0152, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0112, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0241, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to IBM EBCDIC -- POSIX and System V compatible. */ +const u_char a2ibm_POSIX[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0117, 0320, 0241, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; diff --git a/cpukit/libmisc/shell/dd-misc.c b/cpukit/libmisc/shell/dd-misc.c new file mode 100644 index 0000000000..dc5cc0eb09 --- /dev/null +++ b/cpukit/libmisc/shell/dd-misc.c @@ -0,0 +1,112 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)misc.c 8.3 (Berkeley) 4/2/94"; +#endif +#endif /* not lint */ +#if 0 +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/bin/dd/misc.c,v 1.27 2004/04/06 20:06:46 markm Exp $"); +#endif + +#include <sys/types.h> +#include <sys/time.h> + +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "dd.h" +#include "extern-dd.h" + +void +summary(rtems_shell_dd_globals* globals) +{ + struct timeval tv; + double secs; + char buf[100]; + + (void)gettimeofday(&tv, (struct timezone *)NULL); + secs = tv.tv_sec + tv.tv_usec * 1e-6 - st.start; + if (secs < 1e-6) + secs = 1e-6; + /* Use snprintf(3) so that we don't reenter stdio(3). */ + (void)snprintf(buf, sizeof(buf), + "%ju+%ju records in\n%ju+%ju records out\n", + st.in_full, st.in_part, st.out_full, st.out_part); + (void)write(STDERR_FILENO, buf, strlen(buf)); + if (st.swab) { + (void)snprintf(buf, sizeof(buf), "%ju odd length swab %s\n", + st.swab, (st.swab == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + if (st.trunc) { + (void)snprintf(buf, sizeof(buf), "%ju truncated %s\n", + st.trunc, (st.trunc == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + (void)snprintf(buf, sizeof(buf), + "%ju bytes transferred in %.6f secs (%.0f bytes/sec)\n", + st.bytes, secs, st.bytes / secs); + (void)write(STDERR_FILENO, buf, strlen(buf)); +} + +/* ARGSUSED */ +void +summaryx(rtems_shell_dd_globals* globals, int __unused) +{ + int save_errno = errno; + + summary(globals); + errno = save_errno; +} + +#if RTEMS_REMOVED +/* ARGSUSED */ +void +terminate(int sig) +{ + + summary(); + _exit(sig == 0 ? 0 : 1); +} +#endif diff --git a/cpukit/libmisc/shell/dd-position.c b/cpukit/libmisc/shell/dd-position.c new file mode 100644 index 0000000000..7586faddf8 --- /dev/null +++ b/cpukit/libmisc/shell/dd-position.c @@ -0,0 +1,193 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)position.c 8.3 (Berkeley) 4/2/94"; +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/bin/dd/position.c,v 1.23 2004/04/06 20:06:46 markm Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#if RTEMS_REMOVED +#include <sys/mtio.h> +#endif + +#include <err.h> +#include <errno.h> +#include <inttypes.h> +#include <unistd.h> + +#include "dd.h" +#include "extern-dd.h" + +/* + * Position input/output data streams before starting the copy. Device type + * dependent. Seekable devices use lseek, and the rest position by reading. + * Seeking past the end of file can cause null blocks to be written to the + * output. + */ +void +pos_in(rtems_shell_dd_globals* globals) +{ + off_t cnt; + int warned; + ssize_t nr; + size_t bcnt; + + /* If known to be seekable, try to seek on it. */ + if (in.flags & ISSEEK) { + errno = 0; + if (lseek(in.fd, in.offset * in.dbsz, SEEK_CUR) == -1 && + errno != 0) + err(exit_jump, 1, "%s", in.name); + return; + } + + /* Don't try to read a really weird amount (like negative). */ + if (in.offset < 0) + errx(exit_jump, 1, "%s: illegal offset", "iseek/skip"); + + /* + * Read the data. If a pipe, read until satisfy the number of bytes + * being skipped. No differentiation for reading complete and partial + * blocks for other devices. + */ + for (bcnt = in.dbsz, cnt = in.offset, warned = 0; cnt;) { + if ((nr = read(in.fd, in.db, bcnt)) > 0) { + if (in.flags & ISPIPE) { + if (!(bcnt -= nr)) { + bcnt = in.dbsz; + --cnt; + } + } else + --cnt; + continue; + } + + if (nr == 0) { + if (files_cnt > 1) { + --files_cnt; + continue; + } + errx(exit_jump, 1, "skip reached end of input"); + } + + /* + * Input error -- either EOF with no more files, or I/O error. + * If noerror not set die. POSIX requires that the warning + * message be followed by an I/O display. + */ + if (ddflags & C_NOERROR) { + if (!warned) { + warn("%s", in.name); + warned = 1; + summary(globals); + } + continue; + } + err(exit_jump, 1, "%s", in.name); + } +} + +void +pos_out(rtems_shell_dd_globals* globals) +{ +#if RTEMS_REMOVED + struct mtop t_op; + off_t cnt; + ssize_t n; +#endif + + /* + * If not a tape, try seeking on the file. Seeking on a pipe is + * going to fail, but don't protect the user -- they shouldn't + * have specified the seek operand. + */ + if (out.flags & (ISSEEK | ISPIPE)) { + errno = 0; + if (lseek(out.fd, out.offset * out.dbsz, SEEK_CUR) == -1 && + errno != 0) + err(exit_jump, 1, "%s", out.name); + return; + } + + /* Don't try to read a really weird amount (like negative). */ + if (out.offset < 0) + errx(exit_jump, 1, "%s: illegal offset", "oseek/seek"); + +#if RTEMS_REMOVED + /* If no read access, try using mtio. */ + if (out.flags & NOREAD) { + t_op.mt_op = MTFSR; + t_op.mt_count = out.offset; + + if (ioctl(out.fd, MTIOCTOP, &t_op) == -1) + err(1, "%s", out.name); + return; + } + + /* Read it. */ + for (cnt = 0; cnt < out.offset; ++cnt) { + if ((n = read(out.fd, out.db, out.dbsz)) > 0) + continue; + + if (n == -1) + err(1, "%s", out.name); + + /* + * If reach EOF, fill with NUL characters; first, back up over + * the EOF mark. Note, cnt has not yet been incremented, so + * the EOF read does not count as a seek'd block. + */ + t_op.mt_op = MTBSR; + t_op.mt_count = 1; + if (ioctl(out.fd, MTIOCTOP, &t_op) == -1) + err(1, "%s", out.name); + + while (cnt++ < out.offset) { + n = write(out.fd, out.db, out.dbsz); + if (n == -1) + err(1, "%s", out.name); + if ((size_t)n != out.dbsz) + errx(1, "%s: write failure", out.name); + } + break; + } +#endif +} diff --git a/cpukit/libmisc/shell/dd.h b/cpukit/libmisc/shell/dd.h new file mode 100644 index 0000000000..dc80765d48 --- /dev/null +++ b/cpukit/libmisc/shell/dd.h @@ -0,0 +1,108 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)dd.h 8.3 (Berkeley) 4/2/94 + * $FreeBSD: src/bin/dd/dd.h,v 1.22 2004/08/15 19:10:05 rwatson Exp $ + */ + +#ifndef _DD_H_ +#define _DD_H_ + +#include <stddef.h> + +#define OFF_MAX LONG_MAX + +/* Input/output stream state. */ +typedef struct { + u_char *db; /* buffer address */ + u_char *dbp; /* current buffer I/O address */ + /* XXX ssize_t? */ + size_t dbcnt; /* current buffer byte count */ + size_t dbrcnt; /* last read byte count */ + size_t dbsz; /* buffer size */ + +#define ISCHR 0x01 /* character device (warn on short) */ +#define ISPIPE 0x02 /* pipe-like (see position.c) */ +#define ISTAPE 0x04 /* tape */ +#define ISSEEK 0x08 /* valid to seek on */ +#define NOREAD 0x10 /* not readable */ +#define ISTRUNC 0x20 /* valid to ftruncate() */ + u_int flags; + + const char *name; /* name */ + int fd; /* file descriptor */ + off_t offset; /* # of blocks to skip */ +} IO; + +typedef struct { + uintmax_t in_full; /* # of full input blocks */ + uintmax_t in_part; /* # of partial input blocks */ + uintmax_t out_full; /* # of full output blocks */ + uintmax_t out_part; /* # of partial output blocks */ + uintmax_t trunc; /* # of truncated records */ + uintmax_t swab; /* # of odd-length swab blocks */ + uintmax_t bytes; /* # of bytes written */ + double start; /* start time of dd */ +} STAT; + +/* Flags (in ddflags). */ +#define C_ASCII 0x00001 +#define C_BLOCK 0x00002 +#define C_BS 0x00004 +#define C_CBS 0x00008 +#define C_COUNT 0x00010 +#define C_EBCDIC 0x00020 +#define C_FILES 0x00040 +#define C_IBS 0x00080 +#define C_IF 0x00100 +#define C_LCASE 0x00200 +#define C_NOERROR 0x00400 +#define C_NOTRUNC 0x00800 +#define C_OBS 0x01000 +#define C_OF 0x02000 +#define C_OSYNC 0x04000 +#define C_PAREVEN 0x08000 +#define C_PARNONE 0x100000 +#define C_PARODD 0x200000 +#define C_PARSET 0x400000 +#define C_SEEK 0x800000 +#define C_SKIP 0x1000000 +#define C_SPARSE 0x2000000 +#define C_SWAB 0x4000000 +#define C_SYNC 0x8000000 +#define C_UCASE 0x10000000 +#define C_UNBLOCK 0x20000000 +#define C_FILL 0x40000000 + +#define C_PARITY (C_PAREVEN | C_PARODD | C_PARNONE | C_PARSET) + +#endif diff --git a/cpukit/libmisc/shell/err.c b/cpukit/libmisc/shell/err.c new file mode 100644 index 0000000000..610ea598a2 --- /dev/null +++ b/cpukit/libmisc/shell/err.c @@ -0,0 +1,64 @@ +/* $NetBSD: err.c,v 1.25 2005/09/13 13:51:50 christos Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)err.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: err.c,v 1.25 2005/09/13 13:51:50 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include <err.h> +#include <stdarg.h> + +#ifdef __weak_alias +__weak_alias(err, _err) +#endif + +__dead void +err(jmp_buf* exit_jmp, int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + verr(exit_jmp, eval, fmt, ap); + va_end(ap); +} diff --git a/cpukit/libmisc/shell/err.h b/cpukit/libmisc/shell/err.h new file mode 100644 index 0000000000..f028d3b151 --- /dev/null +++ b/cpukit/libmisc/shell/err.h @@ -0,0 +1,84 @@ +/* $NetBSD: err.h,v 1.14 2005/02/03 04:39:32 perry Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + * + * @(#)err.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _ERR_H_ +#define _ERR_H_ + +/* + * Don't use va_list in the err/warn prototypes. Va_list is typedef'd in two + * places (<machine/varargs.h> and <machine/stdarg.h>), so if we include one + * of them here we may collide with the utility's includes. It's unreasonable + * for utilities to have to include one of them to include err.h, so we get + * _BSD_VA_LIST_ from <machine/ansi.h> and use it. + */ +#include <machine/ansi.h> +#include <sys/cdefs.h> + +#include <stdarg.h> +#define _BSD_VA_LIST_ va_list + +#define __dead + +#define err rtems_shell_err +#define verr rtems_shell_verr +#define errx rtems_shell_errx +#define verrx rtems_shell_verrx +#define warn rtems_shell_warn +#define vwarn rtems_shell_vwarn +#define warnx rtems_shell_warnx +#define vwarnx rtems_shell_vwarnx + +#include <setjmp.h> + +extern jmp_buf rtems_shell_bsd_exit_recover; + +__BEGIN_DECLS +__dead void err(jmp_buf*, int, const char *, ...) + __attribute__((__noreturn__, __format__(__printf__, 3, 4))); +__dead void verr(jmp_buf*, int, const char *, _BSD_VA_LIST_) + __attribute__((__noreturn__, __format__(__printf__, 3, 0))); +__dead void errx(jmp_buf*, int, const char *, ...) + __attribute__((__noreturn__, __format__(__printf__, 3, 4))); +__dead void verrx(jmp_buf*, int, const char *, _BSD_VA_LIST_) + __attribute__((__noreturn__, __format__(__printf__, 3, 0))); +void warn(const char *, ...) + __attribute__((__format__(__printf__, 1, 2))); +void vwarn(const char *, _BSD_VA_LIST_) + __attribute__((__format__(__printf__, 1, 0))); +void warnx(const char *, ...) + __attribute__((__format__(__printf__, 1, 2))); +void vwarnx(const char *, _BSD_VA_LIST_) + __attribute__((__format__(__printf__, 1, 0))); +__END_DECLS + +#endif /* !_ERR_H_ */ diff --git a/cpukit/libmisc/shell/errx.c b/cpukit/libmisc/shell/errx.c new file mode 100644 index 0000000000..04e15e128b --- /dev/null +++ b/cpukit/libmisc/shell/errx.c @@ -0,0 +1,64 @@ +/* $NetBSD: errx.c,v 1.13 2005/09/13 13:51:50 christos Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)err.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: errx.c,v 1.13 2005/09/13 13:51:50 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include <err.h> +#include <stdarg.h> + +#ifdef __weak_alias +__weak_alias(errx, _errx) +#endif + +__dead void +errx(jmp_buf* exit_jmp, int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + verrx(exit_jmp, eval, fmt, ap); + va_end(ap); +} diff --git a/cpukit/libmisc/shell/extern-cp.h b/cpukit/libmisc/shell/extern-cp.h new file mode 100644 index 0000000000..093b30ff10 --- /dev/null +++ b/cpukit/libmisc/shell/extern-cp.h @@ -0,0 +1,91 @@ +/* $NetBSD: extern.h,v 1.12 2005/10/15 18:22:18 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + * + * @(#)extern.h 8.2 (Berkeley) 4/1/94 + */ + +#ifndef _EXTERN_CP_H_ +#define _EXTERN_CP_H_ + +#define PATH_T RTEMS_SHELL_CP_PATH_T + +typedef struct { + char *p_end; /* pointer to NULL at end of path */ + char *target_end; /* pointer to end of target base */ + char p_path[MAXPATHLEN + 1]; /* pointer to the start of a path */ +} PATH_T; + +#include <setjmp.h> + +typedef struct { + PATH_T to; + int info; + uid_t myuid; + int fflag, iflag, lflag, nflag, pflag, vflag; + mode_t myumask; + jmp_buf exit_jmp; +} rtems_shell_cp_globals; + +#define to cp_globals->to +#define info cp_globals->info +#define myuid cp_globals->myuid +#define fflag cp_globals->fflag +#define iflag cp_globals->iflag +#define lflag cp_globals->lflag +#define nflag cp_globals->nflag +#define pflag cp_globals->pflag +#define vflag cp_globals->vflag +#define myumask cp_globals->myumask +#define exit_jump &(cp_globals->exit_jmp) + +#define copy_fifo rtems_shell_cp_copy_fifo +#define copy_file rtems_shell_cp_copy_file +#define copy_link rtems_shell_cp_copy_link +#define copy_special rtems_shell_cp_copy_special +#define set_utimes rtems_shell_cp_set_utimes +#define setfile rtems_shell_cp_setfile +#define usage rtems_shell_cp_usage + +#include <sys/cdefs.h> + +__BEGIN_DECLS +int copy_fifo(rtems_shell_cp_globals* cp_globals, struct stat *, int); +int copy_file(rtems_shell_cp_globals* cp_globals, FTSENT *, int); +int copy_link(rtems_shell_cp_globals* cp_globals, FTSENT *, int); +int copy_special(rtems_shell_cp_globals* cp_globals, struct stat *, int); +int set_utimes(const char *, struct stat *); +int setfile(rtems_shell_cp_globals* cp_globals, struct stat *, int); +int preserve_dir_acls(struct stat *, char *, char *); +int preserve_fd_acls(int, int); +void usage(rtems_shell_cp_globals* cp_globals); + +__END_DECLS + +#endif /* !_EXTERN_H_ */ diff --git a/cpukit/libmisc/shell/extern-dd.h b/cpukit/libmisc/shell/extern-dd.h new file mode 100644 index 0000000000..405d460ef3 --- /dev/null +++ b/cpukit/libmisc/shell/extern-dd.h @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)extern.h 8.3 (Berkeley) 4/2/94 + * $FreeBSD: src/bin/dd/extern.h,v 1.15 2004/08/15 19:10:05 rwatson Exp $ + */ + +#ifndef _EXTERN_DD_H_ +#define _EXTERN_DD_H_ + +#include <setjmp.h> + +typedef struct rtems_shell_dd_globals_t { + IO in, out; /* input/output state */ + STAT st; /* statistics */ + void (*cfunc)(struct rtems_shell_dd_globals_t* globals); /* conversion function */ + uintmax_t cpy_cnt; /* # of blocks to copy */ + u_int ddflags; /* conversion options */ + size_t cbsz; /* conversion block size */ + uintmax_t files_cnt; /* # of files to copy */ + const u_char *ctab; /* conversion table */ + char fill_char; /* Character to fill with if defined */ + u_char casetab[256]; + int exit_code; + jmp_buf exit_jmp; +} rtems_shell_dd_globals; + +#define in globals->in +#define out globals->out +#define st globals->st +#define cfunc globals->cfunc +#define cpy_cnt globals->cpy_cnt +#define ddflags globals->ddflags +#define cbsz globals->cbsz +#define files_cnt globals->files_cnt +#define casetab globals->casetab +#define ctab globals->ctab +#define fill_char globals->fill_char +#define exit_jump &(globals->exit_jmp) + +#define block rtems_shell_dd_block +#define block_close rtems_shell_dd_block_close +#define dd_out rtems_shell_dd_dd_out +#define def rtems_shell_dd_def +#define def_close rtems_shell_dd_def_close +#define jcl rtems_shell_dd_jcl +#define pos_in rtems_shell_dd_pos_in +#define pos_out rtems_shell_dd_pos_out +#define summary rtems_shell_dd_summary +#define summaryx rtems_shell_dd_summaryx +#define terminate rtems_shell_dd_terminate +#define unblock rtems_shell_dd_unblock +#define unblock_close rtems_shell_dd_unblock_close + +void block(rtems_shell_dd_globals* ); +void block_close(rtems_shell_dd_globals* ); +void dd_out(rtems_shell_dd_globals* , int); +void def(rtems_shell_dd_globals* globals); +void def_close(rtems_shell_dd_globals* ); +void jcl(rtems_shell_dd_globals* , char **); +void pos_in(rtems_shell_dd_globals* ); +void pos_out(rtems_shell_dd_globals* ); +void summary(rtems_shell_dd_globals* ); +void summaryx(rtems_shell_dd_globals* , int); +void terminate(int); +void unblock(rtems_shell_dd_globals* globals); +void unblock_close(rtems_shell_dd_globals* globals); + +extern const u_char a2e_32V[256], a2e_POSIX[256]; +extern const u_char e2a_32V[256], e2a_POSIX[256]; +extern const u_char a2ibm_32V[256], a2ibm_POSIX[256]; + +void rtems_shell_dd_exit(rtems_shell_dd_globals* globals, int code); + +#define exit(ec) rtems_shell_dd_exit(globals, ec) + +#endif /* !_EXTERN_H_ */ diff --git a/cpukit/libmisc/shell/extern-ls.h b/cpukit/libmisc/shell/extern-ls.h new file mode 100644 index 0000000000..33f713b66e --- /dev/null +++ b/cpukit/libmisc/shell/extern-ls.h @@ -0,0 +1,192 @@ +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * 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. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)ls.h 8.1 (Berkeley) 5/31/93 + * $FreeBSD: src/bin/ls/ls.h,v 1.23 2008/04/04 03:57:46 grog Exp $ + */ + +#ifndef _EXTERN_LS_H_ +#define _EXTERN_LS_H_ + +#define NO_PRINT 1 +//#define COLORLS 1 + +#include <sys/cdefs.h> +#include <setjmp.h> + +#define major(d) rtems_filesystem_dev_major_t(d) +#define minor(d) rtems_filesystem_dev_minor_t(d) + +const char *user_from_uid(uid_t uid, int nouser); + +typedef struct { + int blocksize; + int termwidth; + int sortkey; + int rval; + int output; + time_t now; + + int f_accesstime; + int f_column; + int f_columnacross; + int f_flags; + int f_grouponly; + int f_humanize; + int f_inode; + int f_listdir; + int f_listdot; + int f_longform; + int f_nonprint; + int f_nosort; + int f_numericonly; + int f_octal; + int f_octal_escape; + int f_recursive; + int f_reversesort; + int f_sectime; + int f_singlecol; + int f_size; + int f_statustime; + int f_stream; + int f_type; + int f_typedir; + int f_whiteout; + + int exit_code; + jmp_buf exit_jmp; +} rtems_shell_ls_globals; + +#define blocksize globals->blocksize +#define termwidth globals->termwidth +#define sortkey globals->sortkey +#define rval globals->rval +#define output globals->output +#define now globals->now + +#define f_accesstime globals->f_accesstime +#define f_column globals->f_column +#define f_columnacross globals->f_columnacross +#define f_flags globals->f_flags +#define f_grouponly globals->f_grouponly +#define f_humanize globals->f_humanize +#define f_inode globals->f_inode +#define f_listdir globals->f_listdir +#define f_listdot globals->f_listdot +#define f_longform globals->f_longform +#define f_nonprint globals->f_nonprint +#define f_nosort globals->f_nosort +#define f_numericonly globals->f_numericonly +#define f_octal globals->f_octal +#define f_octal_escape globals->f_octal_escape +#define f_recursive globals->f_recursive +#define f_reversesort globals->f_reversesort +#define f_sectime globals->f_sectime +#define f_singlecol globals->f_singlecol +#define f_size globals->f_size +#define f_statustime globals->f_statustime +#define f_stream globals->f_stream +#define f_type globals->f_type +#define f_typedir globals->f_typedir +#define f_whiteout globals->f_whiteout + +#define exit_jump &(globals->exit_jmp) + +void rtems_shell_ls_exit(rtems_shell_ls_globals* globals, int code); + +#define exit(ec) rtems_shell_ls_exit(globals, ec) + + +typedef struct { + FTSENT *list; + u_int64_t btotal; + u_int64_t stotal; + int entries; + int maxlen; + int s_block; + int s_flags; + int s_group; + int s_inode; + int s_nlink; + int s_size; + int s_user; + int s_major; + int s_minor; +} DISPLAY; + +typedef struct { + char *user; + char *group; + char *flags; + char data[1]; +} NAMES; + +#define acccmp rtems_shell_ls_acccmp +#define revacccmp rtems_shell_ls_revacccmp +#define modcmp rtems_shell_ls_modcmp +#define revmodcmp rtems_shell_ls_revmodcmp +#define namecmp rtems_shell_ls_namecmp +#define revnamecmp rtems_shell_ls_revnamecmp +#define statcmp rtems_shell_ls_statcmp +#define revstatcmp rtems_shell_ls_revstatcmp +#define sizecmp rtems_shell_ls_sizecmp +#define revsizecmp rtems_shell_ls_revsizecmp +#define printescaped rtems_shell_ls_printescaped +#define printacol rtems_shell_ls_printacol +#define printcol rtems_shell_ls_printcol +#define printlong rtems_shell_ls_printlong +#define printscol rtems_shell_ls_printscol +#define printstream rtems_shell_ls_printstream +#define usage rtems_shell_ls_usage + +int acccmp(const FTSENT *, const FTSENT *); +int revacccmp(const FTSENT *, const FTSENT *); +int modcmp(const FTSENT *, const FTSENT *); +int revmodcmp(const FTSENT *, const FTSENT *); +int namecmp(const FTSENT *, const FTSENT *); +int revnamecmp(const FTSENT *, const FTSENT *); +int statcmp(const FTSENT *, const FTSENT *); +int revstatcmp(const FTSENT *, const FTSENT *); +int sizecmp(const FTSENT *, const FTSENT *); +int revsizecmp(const FTSENT *, const FTSENT *); + +int printescaped(rtems_shell_ls_globals* globals, const char *); +void printacol(rtems_shell_ls_globals* globals, DISPLAY *); +void printcol(rtems_shell_ls_globals* globals, DISPLAY *); +void printlong(rtems_shell_ls_globals* globals, DISPLAY *); +void printscol(rtems_shell_ls_globals* globals, DISPLAY *); +void printstream(rtems_shell_ls_globals* globals, DISPLAY *); +int safe_print(rtems_shell_ls_globals* globals, const char *); +void usage(rtems_shell_ls_globals* globals); + +void strmode(mode_t mode, char *p); + +#endif /* !_EXTERN_H_ */ diff --git a/cpukit/libmisc/shell/fdisk.c b/cpukit/libmisc/shell/fdisk.c new file mode 100644 index 0000000000..37a2e217dc --- /dev/null +++ b/cpukit/libmisc/shell/fdisk.c @@ -0,0 +1,280 @@ +/** + * @file + * + * Block device partition management. + */ + +/* + * Copyright (c) 2009 + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <rtems/bdpart.h> +#include <rtems/error.h> +#include <rtems/shell.h> + +#define RTEMS_BDPART_SHELL_ERROR( fmt, ...) \ + do { \ + printf( "error: " fmt "\n", ##__VA_ARGS__); \ + return -1; \ + } while (0) + +#define RTEMS_BDPART_SHELL_ERROR_SC( sc, fmt, ...) \ + if ((sc) != RTEMS_SUCCESSFUL) { \ + printf( "error: " fmt ": %s\n", ##__VA_ARGS__, rtems_status_text( sc)); \ + return -1; \ + } + +typedef enum { + RTEMS_BDPART_SHELL_FS, + RTEMS_BDPART_SHELL_N, + RTEMS_BDPART_SHELL_MBR, + RTEMS_BDPART_SHELL_GPT +} rtems_bdpart_shell_state; + +static const char rtems_bdpart_shell_usage [] = + "disk format and utility functions\n" + "\n" + "fdisk DISK_NAME\n" + "\tprints the partition table\n" + "\n" + "fdisk DISK_NAME [FS N1 [N2 ... ]] ... [write] [FORMAT]\n" + "\tcreates a new partition table\n" + "\n" + "fdisk DISK_NAME register\n" + "\tcreates a logical disk for each partition of the disk\n" + "\n" + "fdisk DISK_NAME unregister\n" + "\tdeletes the logical disks associated with the partitions of the disk\n" + "\n" + "fdisk DISK_NAME mount\n" + "\tmounts the file system of each partition of the disk\n" + "\n" + "fdisk DISK_NAME unmount\n" + "\tunmounts the file system of each partition of the disk\n" + "\n" + "option values:\n" + "\tDISK_NAME: absolute path to disk device like '/dev/hda'\n" + "\tN*: weights of positive integers\n" + "\tFS: 0x00 ... 0xff, fat12, fat16, fat32, data\n" + "\twrite: write the new partition table to the disk\n" + "\tFORMAT: mbr [[no]dos], gpt"; + +static int rtems_bdpart_shell_main( int argc, char **argv) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_bdpart_format format; + rtems_bdpart_partition pt [RTEMS_BDPART_PARTITION_NUMBER_HINT]; + unsigned dist [RTEMS_BDPART_PARTITION_NUMBER_HINT]; + size_t count = RTEMS_BDPART_PARTITION_NUMBER_HINT; + const char *disk_name = NULL; + const char *mount_base = "/mnt"; + bool do_create = false; + bool do_read = false; + bool do_write = false; + bool do_register = false; + bool do_unregister = false; + bool do_mount = false; + bool do_unmount = false; + bool do_dump = false; + + if (argc < 2) { + puts( rtems_bdpart_shell_usage); + return -1; + } + + disk_name = argv [1]; + + if (argc == 2) { + do_read = true; + do_dump = true; + } else if (argc == 3) { + /* Check option */ + if (strcmp( argv [2], "register") == 0) { + do_read = true; + do_register = true; + } else if (strcmp( argv [2], "unregister") == 0) { + do_read = true; + do_unregister = true; + } else if (strcmp( argv [2], "mount") == 0) { + do_read = true; + do_mount = true; + } else if (strcmp( argv [2], "unmount") == 0) { + do_read = true; + do_unmount = true; + } else { + RTEMS_BDPART_SHELL_ERROR( "unexpected option: %s", argv [2]); + } + } else { + rtems_bdpart_shell_state state = RTEMS_BDPART_SHELL_FS; + uint8_t current_type = RTEMS_BDPART_MBR_FAT_32; + size_t i = 0; + int ai = 0; + + /* Clear partition table */ + memset( pt, 0, sizeof( pt)); + + /* Default format */ + format.type = RTEMS_BDPART_FORMAT_MBR; + format.mbr.disk_id = 0; + format.mbr.dos_compatibility = true; + + for (ai = 2; ai < argc; ++ai) { + char *s = argv [ai]; + unsigned long v = 0; + char *end = NULL; + + if (strlen( s) == 0) { + continue; + } else if (strcmp( s, "write") == 0) { + do_write = true; + continue; + } else if (strcmp( s, "mbr") == 0) { + state = RTEMS_BDPART_SHELL_MBR; + format.type = RTEMS_BDPART_FORMAT_MBR; + continue; + } else if (strcmp( s, "gpt") == 0) { + state = RTEMS_BDPART_SHELL_GPT; + format.type = RTEMS_BDPART_FORMAT_GPT; + continue; + } + + switch (state) { + case RTEMS_BDPART_SHELL_FS: + v = strtoul( s, &end, 16); + if (*end == '\0') { + if (v <= 0xffU) { + current_type = (uint8_t) v; + } else { + RTEMS_BDPART_SHELL_ERROR( "type value out of range: %s", argv [ai]); + } + } else if (strcmp( s, "fat32") == 0) { + current_type = RTEMS_BDPART_MBR_FAT_32; + } else if (strcmp( s, "data") == 0) { + current_type = RTEMS_BDPART_MBR_DATA; + } else if (strcmp( s, "fat16") == 0) { + current_type = RTEMS_BDPART_MBR_FAT_16; + } else if (strcmp( s, "fat12") == 0) { + current_type = RTEMS_BDPART_MBR_FAT_12; + } else { + RTEMS_BDPART_SHELL_ERROR( "unexpected option: %s", argv [ai]); + } + state = RTEMS_BDPART_SHELL_N; + break; + case RTEMS_BDPART_SHELL_N: + v = strtoul( s, &end, 10); + if (*end == '\0') { + rtems_bdpart_to_partition_type( current_type, pt [i].type); + dist [i] = v; + if (i < count) { + ++i; + } else { + RTEMS_BDPART_SHELL_ERROR( "too many partitions"); + } + } else { + --ai; + state = RTEMS_BDPART_SHELL_FS; + } + break; + case RTEMS_BDPART_SHELL_MBR: + if (strcmp( s, "dos") == 0) { + format.mbr.dos_compatibility = true; + } else if (strcmp( s, "nodos") == 0) { + format.mbr.dos_compatibility = false; + } else { + RTEMS_BDPART_SHELL_ERROR( "unexpected option: %s", argv [ai]); + } + break; + case RTEMS_BDPART_SHELL_GPT: + RTEMS_BDPART_SHELL_ERROR( "unexpected option: %s", argv [ai]); + default: + RTEMS_BDPART_SHELL_ERROR( "fdisk interal error"); + } + } + + /* Partition number */ + count = i; + + /* Actions */ + do_create = true; + do_dump = true; + if (do_write) { + do_read = true; + } + } + + if (do_create) { + /* Create partitions */ + sc = rtems_bdpart_create( disk_name, &format, pt, dist, count); + RTEMS_BDPART_SHELL_ERROR_SC( sc, "cannot create partitions for '%s'", disk_name); + } + + if (do_write) { + /* Write partitions */ + sc = rtems_bdpart_write( disk_name, &format, pt, count); + RTEMS_BDPART_SHELL_ERROR_SC( sc, "cannot write partitions to '%s'", disk_name); + } + + if (do_read) { + /* Read partitions */ + count = RTEMS_BDPART_PARTITION_NUMBER_HINT; + sc = rtems_bdpart_read( disk_name, &format, pt, &count); + RTEMS_BDPART_SHELL_ERROR_SC( sc, "cannot read partitions from '%s'", disk_name); + } + + if (do_register) { + /* Register partitions */ + sc = rtems_bdpart_register( disk_name, pt, count); + RTEMS_BDPART_SHELL_ERROR_SC( sc, "cannot register partitions of '%s'", disk_name); + } + + if (do_unregister) { + /* Unregister partitions */ + sc = rtems_bdpart_unregister( disk_name, pt, count); + RTEMS_BDPART_SHELL_ERROR_SC( sc, "cannot unregister partitions of '%s'", disk_name); + } + + if (do_mount) { + /* Mount partitions */ + sc = rtems_bdpart_mount( disk_name, pt, count, mount_base); + RTEMS_BDPART_SHELL_ERROR_SC( sc, "cannot mount partitions of '%s' to '%s'", disk_name, mount_base); + } + + if (do_unmount) { + /* Unmount partitions */ + sc = rtems_bdpart_unmount( disk_name, pt, count, mount_base); + RTEMS_BDPART_SHELL_ERROR_SC( sc, "cannot unmount partitions of '%s'", disk_name); + } + + if (do_dump) { + /* Dump partitions */ + rtems_bdpart_dump( pt, count); + } + + return 0; +} + +struct rtems_shell_cmd_tt rtems_shell_FDISK_Command = { + .name = "fdisk", + .usage = rtems_bdpart_shell_usage, + .topic = "files", + .command = rtems_bdpart_shell_main, + .alias = NULL, + .next = NULL +}; diff --git a/cpukit/libmisc/shell/filemode.c b/cpukit/libmisc/shell/filemode.c new file mode 100644 index 0000000000..9a5d0440b4 --- /dev/null +++ b/cpukit/libmisc/shell/filemode.c @@ -0,0 +1,154 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. 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. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strmode.c 8.3 (Berkeley) 8/15/94"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/lib/libc/string/strmode.c,v 1.5 2007/01/09 00:28:12 imp Exp $"); + +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> + +void +strmode( + mode_t mode, + char *p) +{ + /* print type */ + switch (mode & S_IFMT) { + case S_IFDIR: /* directory */ + *p++ = 'd'; + break; + case S_IFCHR: /* character special */ + *p++ = 'c'; + break; + case S_IFBLK: /* block special */ + *p++ = 'b'; + break; + case S_IFREG: /* regular */ + *p++ = '-'; + break; + case S_IFLNK: /* symbolic link */ + *p++ = 'l'; + break; + case S_IFSOCK: /* socket */ + *p++ = 's'; + break; +#ifdef S_IFIFO + case S_IFIFO: /* fifo */ + *p++ = 'p'; + break; +#endif +#ifdef S_IFWHT + case S_IFWHT: /* whiteout */ + *p++ = 'w'; + break; +#endif + default: /* unknown */ + *p++ = '?'; + break; + } + /* usr */ + if (mode & S_IRUSR) + *p++ = 'r'; + else + *p++ = '-'; + if (mode & S_IWUSR) + *p++ = 'w'; + else + *p++ = '-'; + switch (mode & (S_IXUSR | S_ISUID)) { + case 0: + *p++ = '-'; + break; + case S_IXUSR: + *p++ = 'x'; + break; + case S_ISUID: + *p++ = 'S'; + break; + case S_IXUSR | S_ISUID: + *p++ = 's'; + break; + } + /* group */ + if (mode & S_IRGRP) + *p++ = 'r'; + else + *p++ = '-'; + if (mode & S_IWGRP) + *p++ = 'w'; + else + *p++ = '-'; + switch (mode & (S_IXGRP | S_ISGID)) { + case 0: + *p++ = '-'; + break; + case S_IXGRP: + *p++ = 'x'; + break; + case S_ISGID: + *p++ = 'S'; + break; + case S_IXGRP | S_ISGID: + *p++ = 's'; + break; + } + /* other */ + if (mode & S_IROTH) + *p++ = 'r'; + else + *p++ = '-'; + if (mode & S_IWOTH) + *p++ = 'w'; + else + *p++ = '-'; + switch (mode & (S_IXOTH | S_ISVTX)) { + case 0: + *p++ = '-'; + break; + case S_IXOTH: + *p++ = 'x'; + break; + case S_ISVTX: + *p++ = 'T'; + break; + case S_IXOTH | S_ISVTX: + *p++ = 't'; + break; + } + *p++ = ' '; /* will be a '+' if ACL's implemented */ + *p = '\0'; +} diff --git a/cpukit/libmisc/shell/fts.c b/cpukit/libmisc/shell/fts.c new file mode 100644 index 0000000000..2fd91b874c --- /dev/null +++ b/cpukit/libmisc/shell/fts.c @@ -0,0 +1,1244 @@ +/* $NetBSD: fts.c,v 1.40 2009/11/02 17:17:34 stacktic Exp $ */ + +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94"; +#else +__RCSID("$NetBSD: fts.c,v 1.40 2009/11/02 17:17:34 stacktic Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#ifndef __rtems__ +#include "namespace.h" +#endif +#include <limits.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <assert.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> + +#define _DIAGASSERT(a) +#undef FTS_WHITEOUT +#define dirfd(dp) __dirfd(dp) + +#if ! HAVE_NBTOOL_CONFIG_H +#define HAVE_STRUCT_DIRENT_D_NAMLEN +#endif + +static FTSENT *fts_alloc(FTS *, const char *, size_t); +static FTSENT *fts_build(FTS *, int); +static void fts_free(FTSENT *); +static void fts_lfree(FTSENT *); +static void fts_load(FTS *, FTSENT *); +static size_t fts_maxarglen(char * const *); +static size_t fts_pow2(size_t); +static int fts_palloc(FTS *, size_t); +static void fts_padjust(FTS *, FTSENT *); +static FTSENT *fts_sort(FTS *, FTSENT *, size_t); +static unsigned short fts_stat(FTS *, FTSENT *, int); +static int fts_safe_changedir(const FTS *, const FTSENT *, int, + const char *); + +#if defined(ALIGNBYTES) && defined(ALIGN) +#define FTS_ALLOC_ALIGNED 1 +/* FIXME: Redefine because some versions of + * RTEMS newlib and the BSDs ship a broken ALIGN */ +#undef ALIGN +#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) & ~ALIGNBYTES) +#else +#undef FTS_ALLOC_ALIGNED +#endif + +#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) + +#define CLR(opt) (sp->fts_options &= ~(opt)) +#define ISSET(opt) (sp->fts_options & (opt)) +#define SET(opt) (sp->fts_options |= (opt)) + +#define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path)) +#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) + +/* fts_build flags */ +#define BCHILD 1 /* fts_children */ +#define BNAMES 2 /* fts_children, names only */ +#define BREAD 3 /* fts_read */ + +#ifndef DTF_HIDEW +#undef FTS_WHITEOUT +#endif + +FTS * +fts_open(char * const *argv, int options, + int (*compar)(const FTSENT **, const FTSENT **)) +{ + FTS *sp; + FTSENT *p, *root; + size_t nitems; + FTSENT *parent, *tmp = NULL; /* pacify gcc */ + size_t len; + + _DIAGASSERT(argv != NULL); + + /* Options check. */ + if (options & ~FTS_OPTIONMASK) { + errno = EINVAL; + return (NULL); + } + + /* Allocate/initialize the stream */ + if ((sp = malloc((unsigned int)sizeof(FTS))) == NULL) + return (NULL); + memset(sp, 0, sizeof(FTS)); + sp->fts_compar = compar; + sp->fts_options = options; + + /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ + if (ISSET(FTS_LOGICAL)) + SET(FTS_NOCHDIR); + + /* + * Start out with 1K of path space, and enough, in any case, + * to hold the user's paths. + */ + if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) + goto mem1; + + /* Allocate/initialize root's parent. */ + if ((parent = fts_alloc(sp, "", 0)) == NULL) + goto mem2; + parent->fts_level = FTS_ROOTPARENTLEVEL; + + /* Allocate/initialize root(s). */ + for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { + /* Don't allow zero-length paths. */ + if ((len = strlen(*argv)) == 0) { + errno = ENOENT; + goto mem3; + } + + if ((p = fts_alloc(sp, *argv, len)) == NULL) + goto mem3; + p->fts_level = FTS_ROOTLEVEL; + p->fts_parent = parent; + p->fts_accpath = p->fts_name; + p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); + + /* Command-line "." and ".." are real directories. */ + if (p->fts_info == FTS_DOT) + p->fts_info = FTS_D; + + /* + * If comparison routine supplied, traverse in sorted + * order; otherwise traverse in the order specified. + */ + if (compar) { + p->fts_link = root; + root = p; + } else { + p->fts_link = NULL; + if (root == NULL) + tmp = root = p; + else { + tmp->fts_link = p; + tmp = p; + } + } + } + if (compar && nitems > 1) + root = fts_sort(sp, root, nitems); + + /* + * Allocate a dummy pointer and make fts_read think that we've just + * finished the node before the root(s); set p->fts_info to FTS_INIT + * so that everything about the "current" node is ignored. + */ + if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) + goto mem3; + sp->fts_cur->fts_link = root; + sp->fts_cur->fts_info = FTS_INIT; + + /* + * If using chdir(2), grab a file descriptor pointing to dot to insure + * that we can get back here; this could be avoided for some paths, + * but almost certainly not worth the effort. Slashes, symbolic links, + * and ".." are all fairly nasty problems. Note, if we can't get the + * descriptor we run anyway, just more slowly. + */ + if (!ISSET(FTS_NOCHDIR)) { + if ((sp->fts_rfd = open(".", O_RDONLY, 0)) == -1) + SET(FTS_NOCHDIR); + else if (fcntl(sp->fts_rfd, F_SETFD, FD_CLOEXEC) == -1) { + close(sp->fts_rfd); + SET(FTS_NOCHDIR); + } + } + + if (nitems == 0) + fts_free(parent); + + return (sp); + +mem3: fts_lfree(root); + fts_free(parent); +mem2: free(sp->fts_path); +mem1: free(sp); + return (NULL); +} + +static void +fts_load(FTS *sp, FTSENT *p) +{ + size_t len; + char *cp; + + _DIAGASSERT(sp != NULL); + _DIAGASSERT(p != NULL); + + /* + * Load the stream structure for the next traversal. Since we don't + * actually enter the directory until after the preorder visit, set + * the fts_accpath field specially so the chdir gets done to the right + * place and the user can access the first node. From fts_open it's + * known that the path will fit. + */ + len = p->fts_pathlen = p->fts_namelen; + memmove(sp->fts_path, p->fts_name, len + 1); + if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { + len = strlen(++cp); + memmove(p->fts_name, cp, len + 1); + p->fts_namelen = len; + } + p->fts_accpath = p->fts_path = sp->fts_path; + sp->fts_dev = p->fts_dev; +} + +int +fts_close(FTS *sp) +{ + FTSENT *freep, *p; + int saved_errno = 0; + + _DIAGASSERT(sp != NULL); + + /* + * This still works if we haven't read anything -- the dummy structure + * points to the root list, so we step through to the end of the root + * list which has a valid parent pointer. + */ + if (sp->fts_cur) { + if (sp->fts_cur->fts_flags & FTS_SYMFOLLOW) + (void)close(sp->fts_cur->fts_symfd); + for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { + freep = p; + p = p->fts_link ? p->fts_link : p->fts_parent; + fts_free(freep); + } + fts_free(p); + } + + /* Free up child linked list, sort array, path buffer. */ + if (sp->fts_child) + fts_lfree(sp->fts_child); + if (sp->fts_array) + free(sp->fts_array); + free(sp->fts_path); + + /* Return to original directory, save errno if necessary. */ + if (!ISSET(FTS_NOCHDIR)) { + if (fchdir(sp->fts_rfd) == -1) + saved_errno = errno; + (void)close(sp->fts_rfd); + } + + /* Free up the stream pointer. */ + free(sp); + if (saved_errno) { + errno = saved_errno; + return -1; + } + + return 0; +} + +#if !defined(__FTS_COMPAT_TAILINGSLASH) + +/* + * Special case of "/" at the end of the path so that slashes aren't + * appended which would cause paths to be written as "....//foo". + */ +#define NAPPEND(p) \ + (p->fts_path[p->fts_pathlen - 1] == '/' \ + ? p->fts_pathlen - 1 : p->fts_pathlen) + +#else /* !defined(__FTS_COMPAT_TAILINGSLASH) */ + +/* + * compatibility with the old behaviour. + * + * Special case a root of "/" so that slashes aren't appended which would + * cause paths to be written as "//foo". + */ + +#define NAPPEND(p) \ + (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ + p->fts_path[0] == '/' ? 0 : p->fts_pathlen) + +#endif /* !defined(__FTS_COMPAT_TAILINGSLASH) */ + +FTSENT * +fts_read(FTS *sp) +{ + FTSENT *p, *tmp; + int instr; + char *t; + int saved_errno; + + _DIAGASSERT(sp != NULL); + + /* If finished or unrecoverable error, return NULL. */ + if (sp->fts_cur == NULL || ISSET(FTS_STOP)) + return (NULL); + + /* Set current node pointer. */ + p = sp->fts_cur; + + /* Save and zero out user instructions. */ + instr = p->fts_instr; + p->fts_instr = FTS_NOINSTR; + + /* Any type of file may be re-visited; re-stat and re-turn. */ + if (instr == FTS_AGAIN) { + p->fts_info = fts_stat(sp, p, 0); + return (p); + } + + /* + * Following a symlink -- SLNONE test allows application to see + * SLNONE and recover. If indirecting through a symlink, have + * keep a pointer to current location. If unable to get that + * pointer, follow fails. + */ + if (instr == FTS_FOLLOW && + (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { + p->fts_info = fts_stat(sp, p, 1); + if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { + if ((p->fts_symfd = open(".", O_RDONLY, 0)) == -1) { + p->fts_errno = errno; + p->fts_info = FTS_ERR; + } else if (fcntl(p->fts_symfd, F_SETFD, FD_CLOEXEC) == -1) { + p->fts_errno = errno; + p->fts_info = FTS_ERR; + close(p->fts_symfd); + } else + p->fts_flags |= FTS_SYMFOLLOW; + } + return (p); + } + + /* Directory in pre-order. */ + if (p->fts_info == FTS_D) { + /* If skipped or crossed mount point, do post-order visit. */ + if (instr == FTS_SKIP || + (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) { + if (p->fts_flags & FTS_SYMFOLLOW) + (void)close(p->fts_symfd); + if (sp->fts_child) { + fts_lfree(sp->fts_child); + sp->fts_child = NULL; + } + p->fts_info = FTS_DP; + return (p); + } + + /* Rebuild if only read the names and now traversing. */ + if (sp->fts_child && ISSET(FTS_NAMEONLY)) { + CLR(FTS_NAMEONLY); + fts_lfree(sp->fts_child); + sp->fts_child = NULL; + } + + /* + * Cd to the subdirectory. + * + * If have already read and now fail to chdir, whack the list + * to make the names come out right, and set the parent errno + * so the application will eventually get an error condition. + * Set the FTS_DONTCHDIR flag so that when we logically change + * directories back to the parent we don't do a chdir. + * + * If haven't read do so. If the read fails, fts_build sets + * FTS_STOP or the fts_info field of the node. + */ + if (sp->fts_child) { + if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { + p->fts_errno = errno; + p->fts_flags |= FTS_DONTCHDIR; + for (p = sp->fts_child; p; p = p->fts_link) + p->fts_accpath = + p->fts_parent->fts_accpath; + } + } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { + if (ISSET(FTS_STOP)) + return (NULL); + return (p); + } + p = sp->fts_child; + sp->fts_child = NULL; + goto name; + } + + /* Move to the next node on this level. */ +next: tmp = p; + if ((p = p->fts_link) != NULL) { + fts_free(tmp); + + /* + * If reached the top, return to the original directory, and + * load the paths for the next root. + */ + if (p->fts_level == FTS_ROOTLEVEL) { + if (FCHDIR(sp, sp->fts_rfd)) { + SET(FTS_STOP); + return (NULL); + } + fts_load(sp, p); + return (sp->fts_cur = p); + } + + /* + * User may have called fts_set on the node. If skipped, + * ignore. If followed, get a file descriptor so we can + * get back if necessary. + */ + if (p->fts_instr == FTS_SKIP) + goto next; + if (p->fts_instr == FTS_FOLLOW) { + p->fts_info = fts_stat(sp, p, 1); + if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { + if ((p->fts_symfd = + open(".", O_RDONLY, 0)) == -1) { + p->fts_errno = errno; + p->fts_info = FTS_ERR; + } else if (fcntl(p->fts_symfd, F_SETFD, FD_CLOEXEC) == -1) { + p->fts_errno = errno; + p->fts_info = FTS_ERR; + close(p->fts_symfd); + } else + p->fts_flags |= FTS_SYMFOLLOW; + } + p->fts_instr = FTS_NOINSTR; + } + +name: t = sp->fts_path + NAPPEND(p->fts_parent); + *t++ = '/'; + memmove(t, p->fts_name, (size_t)(p->fts_namelen + 1)); + return (sp->fts_cur = p); + } + + /* Move up to the parent node. */ + p = tmp->fts_parent; + fts_free(tmp); + + if (p->fts_level == FTS_ROOTPARENTLEVEL) { + /* + * Done; free everything up and set errno to 0 so the user + * can distinguish between error and EOF. + */ + fts_free(p); + errno = 0; + return (sp->fts_cur = NULL); + } + + /* Nul terminate the pathname. */ + sp->fts_path[p->fts_pathlen] = '\0'; + + /* + * Return to the parent directory. If at a root node or came through + * a symlink, go back through the file descriptor. Otherwise, cd up + * one directory. + */ + if (p->fts_level == FTS_ROOTLEVEL) { + if (FCHDIR(sp, sp->fts_rfd)) { + SET(FTS_STOP); + return (NULL); + } + } else if (p->fts_flags & FTS_SYMFOLLOW) { + if (FCHDIR(sp, p->fts_symfd)) { + saved_errno = errno; + (void)close(p->fts_symfd); + errno = saved_errno; + SET(FTS_STOP); + return (NULL); + } + (void)close(p->fts_symfd); + } else if (!(p->fts_flags & FTS_DONTCHDIR) && + fts_safe_changedir(sp, p->fts_parent, -1, "..")) { + SET(FTS_STOP); + return (NULL); + } + p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; + return (sp->fts_cur = p); +} + +/* + * Fts_set takes the stream as an argument although it's not used in this + * implementation; it would be necessary if anyone wanted to add global + * semantics to fts using fts_set. An error return is allowed for similar + * reasons. + */ +/* ARGSUSED */ +int +fts_set(FTS *sp, FTSENT *p, int instr) +{ + + _DIAGASSERT(sp != NULL); + _DIAGASSERT(p != NULL); + + if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && + instr != FTS_NOINSTR && instr != FTS_SKIP) { + errno = EINVAL; + return (1); + } + p->fts_instr = instr; + return (0); +} + +FTSENT * +fts_children(FTS *sp, int instr) +{ + FTSENT *p; + int fd; + + _DIAGASSERT(sp != NULL); + + if (instr && instr != FTS_NAMEONLY) { + errno = EINVAL; + return (NULL); + } + + /* Set current node pointer. */ + p = sp->fts_cur; + + /* + * Errno set to 0 so user can distinguish empty directory from + * an error. + */ + errno = 0; + + /* Fatal errors stop here. */ + if (ISSET(FTS_STOP)) + return (NULL); + + /* Return logical hierarchy of user's arguments. */ + if (p->fts_info == FTS_INIT) + return (p->fts_link); + + /* + * If not a directory being visited in pre-order, stop here. Could + * allow FTS_DNR, assuming the user has fixed the problem, but the + * same effect is available with FTS_AGAIN. + */ + if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) + return (NULL); + + /* Free up any previous child list. */ + if (sp->fts_child) + fts_lfree(sp->fts_child); + + if (instr == FTS_NAMEONLY) { + SET(FTS_NAMEONLY); + instr = BNAMES; + } else + instr = BCHILD; + + /* + * If using chdir on a relative path and called BEFORE fts_read does + * its chdir to the root of a traversal, we can lose -- we need to + * chdir into the subdirectory, and we don't know where the current + * directory is, so we can't get back so that the upcoming chdir by + * fts_read will work. + */ + if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || + ISSET(FTS_NOCHDIR)) + return (sp->fts_child = fts_build(sp, instr)); + + if ((fd = open(".", O_RDONLY, 0)) == -1) + return (sp->fts_child = NULL); + sp->fts_child = fts_build(sp, instr); + if (fchdir(fd)) { + (void)close(fd); + return (NULL); + } + (void)close(fd); + return (sp->fts_child); +} + +/* + * This is the tricky part -- do not casually change *anything* in here. The + * idea is to build the linked list of entries that are used by fts_children + * and fts_read. There are lots of special cases. + * + * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is + * set and it's a physical walk (so that symbolic links can't be directories), + * we can do things quickly. First, if it's a 4.4BSD file system, the type + * of the file is in the directory entry. Otherwise, we assume that the number + * of subdirectories in a node is equal to the number of links to the parent. + * The former skips all stat calls. The latter skips stat calls in any leaf + * directories and for any files after the subdirectories in the directory have + * been found, cutting the stat calls by about 2/3. + */ +static FTSENT * +fts_build(FTS *sp, int type) +{ + struct dirent *dp; + FTSENT *p, *head; + size_t nitems; + FTSENT *cur, *tail; + DIR *dirp; + void *oldaddr; + size_t dnamlen; + int cderrno, descend, level, nlinks, saved_errno, nostat, doadjust; + size_t len, maxlen; +#ifdef FTS_WHITEOUT + int oflag; +#endif + char *cp = NULL; /* pacify gcc */ + + _DIAGASSERT(sp != NULL); + + /* Set current node pointer. */ + cur = sp->fts_cur; + + /* + * Open the directory for reading. If this fails, we're done. + * If being called from fts_read, set the fts_info field. + */ +#ifdef FTS_WHITEOUT + if (ISSET(FTS_WHITEOUT)) + oflag = DTF_NODUP|DTF_REWIND; + else + oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND; +#else +#define __opendir2(path, flag) opendir(path) +#endif + if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) { + if (type == BREAD) { + cur->fts_info = FTS_DNR; + cur->fts_errno = errno; + } + return (NULL); + } + + /* + * Nlinks is the number of possible entries of type directory in the + * directory if we're cheating on stat calls, 0 if we're not doing + * any stat calls at all, -1 if we're doing stats on everything. + */ + if (type == BNAMES) { + nlinks = 0; + nostat = 1; + } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) { + nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); + nostat = 1; + } else { + nlinks = -1; + nostat = 0; + } + +#ifdef notdef + (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); + (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", + ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); +#endif + /* + * If we're going to need to stat anything or we want to descend + * and stay in the directory, chdir. If this fails we keep going, + * but set a flag so we don't chdir after the post-order visit. + * We won't be able to stat anything, but we can still return the + * names themselves. Note, that since fts_read won't be able to + * chdir into the directory, it will have to return different path + * names than before, i.e. "a/b" instead of "b". Since the node + * has already been visited in pre-order, have to wait until the + * post-order visit to return the error. There is a special case + * here, if there was nothing to stat then it's not an error to + * not be able to stat. This is all fairly nasty. If a program + * needed sorted entries or stat information, they had better be + * checking FTS_NS on the returned nodes. + */ + cderrno = 0; + if (nlinks || type == BREAD) { + if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) { + if (nlinks && type == BREAD) + cur->fts_errno = errno; + cur->fts_flags |= FTS_DONTCHDIR; + descend = 0; + cderrno = errno; + } else + descend = 1; + } else + descend = 0; + + /* + * Figure out the max file name length that can be stored in the + * current path -- the inner loop allocates more path as necessary. + * We really wouldn't have to do the maxlen calculations here, we + * could do them in fts_read before returning the path, but it's a + * lot easier here since the length is part of the dirent structure. + * + * If not changing directories set a pointer so that can just append + * each new name into the path. + */ + len = NAPPEND(cur); + if (ISSET(FTS_NOCHDIR)) { + cp = sp->fts_path + len; + *cp++ = '/'; + } + len++; + maxlen = sp->fts_pathlen - len; + +#if defined(__FTS_COMPAT_LEVEL) + if (cur->fts_level == SHRT_MAX) { + (void)closedir(dirp); + cur->fts_info = FTS_ERR; + SET(FTS_STOP); + errno = ENAMETOOLONG; + return (NULL); + } +#endif + + level = cur->fts_level + 1; + + /* Read the directory, attaching each entry to the `link' pointer. */ + doadjust = 0; + for (head = tail = NULL, nitems = 0; (dp = readdir(dirp)) != NULL;) { + + if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) + continue; + +#if defined(HAVE_STRUCT_DIRENT_D_NAMLEN) + dnamlen = dp->d_namlen; +#else + dnamlen = strlen(dp->d_name); +#endif + if ((p = fts_alloc(sp, dp->d_name, dnamlen)) == NULL) + goto mem1; + if (dnamlen >= maxlen) { /* include space for NUL */ + oldaddr = sp->fts_path; + if (fts_palloc(sp, dnamlen + len + 1)) { + /* + * No more memory for path or structures. Save + * errno, free up the current structure and the + * structures already allocated. + */ +mem1: saved_errno = errno; + if (p) + fts_free(p); + fts_lfree(head); + (void)closedir(dirp); + errno = saved_errno; + cur->fts_info = FTS_ERR; + SET(FTS_STOP); + return (NULL); + } + /* Did realloc() change the pointer? */ + if (oldaddr != sp->fts_path) { + doadjust = 1; + if (ISSET(FTS_NOCHDIR)) + cp = sp->fts_path + len; + } + maxlen = sp->fts_pathlen - len; + } + +#if defined(__FTS_COMPAT_LENGTH) + if (len + dnamlen >= USHRT_MAX) { + /* + * In an FTSENT, fts_pathlen is an unsigned short + * so it is possible to wraparound here. + * If we do, free up the current structure and the + * structures already allocated, then error out + * with ENAMETOOLONG. + */ + fts_free(p); + fts_lfree(head); + (void)closedir(dirp); + cur->fts_info = FTS_ERR; + SET(FTS_STOP); + errno = ENAMETOOLONG; + return (NULL); + } +#endif + p->fts_level = level; + p->fts_pathlen = len + dnamlen; + p->fts_parent = sp->fts_cur; + +#ifdef FTS_WHITEOUT + if (dp->d_type == DT_WHT) + p->fts_flags |= FTS_ISW; +#endif + + if (cderrno) { + if (nlinks) { + p->fts_info = FTS_NS; + p->fts_errno = cderrno; + } /* else + p->fts_info = FTS_NSOK; + */ + /* Coverity Scan Id 1 says above is dead code */ + p->fts_accpath = cur->fts_accpath; + } else if (nlinks == 0 +#ifdef DT_DIR + || (nostat && + dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) +#endif + ) { + p->fts_accpath = + ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; + p->fts_info = FTS_NSOK; + } else { + /* Build a file name for fts_stat to stat. */ + if (ISSET(FTS_NOCHDIR)) { + p->fts_accpath = p->fts_path; + memmove(cp, p->fts_name, + (size_t)(p->fts_namelen + 1)); + } else + p->fts_accpath = p->fts_name; + /* Stat it. */ + p->fts_info = fts_stat(sp, p, 0); + + /* Decrement link count if applicable. */ + if (nlinks > 0 && (p->fts_info == FTS_D || + p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) + --nlinks; + } + + /* We walk in directory order so "ls -f" doesn't get upset. */ + p->fts_link = NULL; + if (head == NULL) + head = tail = p; + else { + tail->fts_link = p; + tail = p; + } + ++nitems; + } + (void)closedir(dirp); + + /* + * If had to realloc the path, adjust the addresses for the rest + * of the tree. + */ + if (doadjust) + fts_padjust(sp, head); + + /* + * If not changing directories, reset the path back to original + * state. + */ + if (ISSET(FTS_NOCHDIR)) { + if (len == sp->fts_pathlen || nitems == 0) + --cp; + *cp = '\0'; + } + + /* + * If descended after called from fts_children or after called from + * fts_read and nothing found, get back. At the root level we use + * the saved fd; if one of fts_open()'s arguments is a relative path + * to an empty directory, we wind up here with no other way back. If + * can't get back, we're done. + */ + if (descend && (type == BCHILD || !nitems) && + (cur->fts_level == FTS_ROOTLEVEL ? + FCHDIR(sp, sp->fts_rfd) : + fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { + cur->fts_info = FTS_ERR; + SET(FTS_STOP); + return (NULL); + } + + /* If didn't find anything, return NULL. */ + if (!nitems) { + if (type == BREAD) + cur->fts_info = FTS_DP; + return (NULL); + } + + /* Sort the entries. */ + if (sp->fts_compar && nitems > 1) + head = fts_sort(sp, head, nitems); + return (head); +} + +static unsigned short +fts_stat(FTS *sp, FTSENT *p, int follow) +{ + FTSENT *t; + dev_t dev; + __fts_ino_t ino; + __fts_stat_t *sbp, sb; + int saved_errno; + + _DIAGASSERT(sp != NULL); + _DIAGASSERT(p != NULL); + + /* If user needs stat info, stat buffer already allocated. */ + sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; + +#ifdef FTS_WHITEOUT + /* check for whiteout */ + if (p->fts_flags & FTS_ISW) { + if (sbp != &sb) { + memset(sbp, '\0', sizeof (*sbp)); + sbp->st_mode = S_IFWHT; + } + return (FTS_W); + } +#endif + + /* + * If doing a logical walk, or application requested FTS_FOLLOW, do + * a stat(2). If that fails, check for a non-existent symlink. If + * fail, set the errno from the stat call. + */ + if (ISSET(FTS_LOGICAL) || follow) { + if (stat(p->fts_accpath, sbp)) { + saved_errno = errno; + if (!lstat(p->fts_accpath, sbp)) { + errno = 0; + return (FTS_SLNONE); + } + p->fts_errno = saved_errno; + goto err; + } + } else if (lstat(p->fts_accpath, sbp)) { + p->fts_errno = errno; +err: memset(sbp, 0, sizeof(*sbp)); + return (FTS_NS); + } + + if (S_ISDIR(sbp->st_mode)) { + /* + * Set the device/inode. Used to find cycles and check for + * crossing mount points. Also remember the link count, used + * in fts_build to limit the number of stat calls. It is + * understood that these fields are only referenced if fts_info + * is set to FTS_D. + */ + dev = p->fts_dev = sbp->st_dev; + ino = p->fts_ino = sbp->st_ino; + p->fts_nlink = sbp->st_nlink; + + if (ISDOT(p->fts_name)) + return (FTS_DOT); + + /* + * Cycle detection is done by brute force when the directory + * is first encountered. If the tree gets deep enough or the + * number of symbolic links to directories is high enough, + * something faster might be worthwhile. + */ + for (t = p->fts_parent; + t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) + if (ino == t->fts_ino && dev == t->fts_dev) { + p->fts_cycle = t; + return (FTS_DC); + } + return (FTS_D); + } + if (S_ISLNK(sbp->st_mode)) + return (FTS_SL); + if (S_ISREG(sbp->st_mode)) + return (FTS_F); + return (FTS_DEFAULT); +} + +static FTSENT * +fts_sort(FTS *sp, FTSENT *head, size_t nitems) +{ + FTSENT **ap, *p; + + _DIAGASSERT(sp != NULL); + _DIAGASSERT(head != NULL); + + /* + * Construct an array of pointers to the structures and call qsort(3). + * Reassemble the array in the order returned by qsort. If unable to + * sort for memory reasons, return the directory entries in their + * current order. Allocate enough space for the current needs plus + * 40 so don't realloc one entry at a time. + */ + if (nitems > sp->fts_nitems) { + FTSENT **new; + + new = realloc(sp->fts_array, sizeof(FTSENT *) * (nitems + 40)); + if (new == 0) + return (head); + sp->fts_array = new; + sp->fts_nitems = nitems + 40; + } + for (ap = sp->fts_array, p = head; p; p = p->fts_link) + *ap++ = p; + qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), + (int (*)(const void *, const void *))sp->fts_compar); + for (head = *(ap = sp->fts_array); --nitems; ++ap) + ap[0]->fts_link = ap[1]; + ap[0]->fts_link = NULL; + return (head); +} + +static FTSENT * +fts_alloc(FTS *sp, const char *name, size_t namelen) +{ + FTSENT *p; +#if defined(FTS_ALLOC_ALIGNED) + size_t len; +#endif + + _DIAGASSERT(sp != NULL); + _DIAGASSERT(name != NULL); + +#if defined(FTS_ALLOC_ALIGNED) + /* + * The file name is a variable length array and no stat structure is + * necessary if the user has set the nostat bit. Allocate the FTSENT + * structure, the file name and the stat structure in one chunk, but + * be careful that the stat structure is reasonably aligned. Since the + * fts_name field is declared to be of size 1, the fts_name pointer is + * namelen + 2 before the first possible address of the stat structure. + */ + len = sizeof(FTSENT) + namelen; + if (!ISSET(FTS_NOSTAT)) + len += sizeof(*(p->fts_statp)) + ALIGNBYTES; + if ((p = malloc(len)) == NULL) + return (NULL); + + if (!ISSET(FTS_NOSTAT)) + p->fts_statp = (__fts_stat_t *)ALIGN(p->fts_name + namelen + 2); +#else + if ((p = malloc(sizeof(FTSENT) + namelen)) == NULL) + return (NULL); + + if (!ISSET(FTS_NOSTAT)) + if ((p->fts_statp = malloc(sizeof(*(p->fts_statp)))) == NULL) { + free(p); + return (NULL); + } +#endif + + if (ISSET(FTS_NOSTAT)) + p->fts_statp = NULL; + + /* Copy the name plus the trailing NULL. */ + memmove(p->fts_name, name, namelen + 1); + + p->fts_namelen = namelen; + p->fts_path = sp->fts_path; + p->fts_errno = 0; + p->fts_flags = 0; + p->fts_instr = FTS_NOINSTR; + p->fts_number = 0; + p->fts_pointer = NULL; + return (p); +} + +static void +fts_free(FTSENT *p) +{ +#if !defined(FTS_ALLOC_ALIGNED) + if (p->fts_statp) + free(p->fts_statp); +#endif + free(p); +} + +static void +fts_lfree(FTSENT *head) +{ + FTSENT *p; + + /* XXX: head may be NULL ? */ + + /* Free a linked list of structures. */ + while ((p = head) != NULL) { + head = head->fts_link; + fts_free(p); + } +} + +static size_t +fts_pow2(size_t x) +{ + + x--; + x |= x>>1; + x |= x>>2; + x |= x>>4; + x |= x>>8; +#if (SIZEOF_SIZE_T * CHAR_BIT) > 16 + x |= x>>16; +#endif +#if (SIZEOF_SIZE_T * CHAR_BIT) > 32 + x |= x>>32; +#endif +#if (SIZEOF_SIZE_T * CHAR_BIT) > 64 + x |= x>>64; +#endif + x++; + return (x); +} + +/* + * Allow essentially unlimited paths; find, rm, ls should all work on any tree. + * Most systems will allow creation of paths much longer than MAXPATHLEN, even + * though the kernel won't resolve them. Round up the new size to a power of 2, + * so we don't realloc the path 2 bytes at a time. + */ +static int +fts_palloc(FTS *sp, size_t size) +{ + char *new; + + _DIAGASSERT(sp != NULL); + +#ifdef __FTS_COMPAT_LENGTH + /* Protect against fts_pathlen overflow. */ + if (size > USHRT_MAX + 1) { + errno = ENAMETOOLONG; + return (1); + } +#endif + size = fts_pow2(size); + new = realloc(sp->fts_path, size); + if (new == 0) + return (1); + sp->fts_path = new; + sp->fts_pathlen = size; + return (0); +} + +/* + * When the path is realloc'd, have to fix all of the pointers in structures + * already returned. + */ +static void +fts_padjust(FTS *sp, FTSENT *head) +{ + FTSENT *p; + char *addr; + + _DIAGASSERT(sp != NULL); + +#define ADJUST(p) do { \ + if ((p)->fts_accpath != (p)->fts_name) \ + (p)->fts_accpath = \ + addr + ((p)->fts_accpath - (p)->fts_path); \ + (p)->fts_path = addr; \ +} while (/*CONSTCOND*/0) + + addr = sp->fts_path; + + /* Adjust the current set of children. */ + for (p = sp->fts_child; p; p = p->fts_link) + ADJUST(p); + + /* Adjust the rest of the tree, including the current level. */ + for (p = head; p->fts_level >= FTS_ROOTLEVEL;) { + ADJUST(p); + p = p->fts_link ? p->fts_link : p->fts_parent; + } +} + +static size_t +fts_maxarglen(char * const *argv) +{ + size_t len, max; + + _DIAGASSERT(argv != NULL); + + for (max = 0; *argv; ++argv) + if ((len = strlen(*argv)) > max) + max = len; + return (max + 1); +} + +/* + * Change to dir specified by fd or p->fts_accpath without getting + * tricked by someone changing the world out from underneath us. + * Assumes p->fts_dev and p->fts_ino are filled in. + */ +static int +fts_safe_changedir(const FTS *sp, const FTSENT *p, int fd, const char *path) +{ + int oldfd = fd, ret = -1; + __fts_stat_t sb; + + if (ISSET(FTS_NOCHDIR)) + return 0; + + if (oldfd < 0 && (fd = open(path, O_RDONLY)) == -1) + return -1; + + if (fstat(fd, &sb) == -1) + goto bail; + + if (sb.st_ino != p->fts_ino || sb.st_dev != p->fts_dev) { + errno = ENOENT; + goto bail; + } + + ret = fchdir(fd); + +bail: + if (oldfd < 0) { + int save_errno = errno; + (void)close(fd); + errno = save_errno; + } + return ret; +} diff --git a/cpukit/libmisc/shell/fts.h b/cpukit/libmisc/shell/fts.h new file mode 100644 index 0000000000..68e9d46c3e --- /dev/null +++ b/cpukit/libmisc/shell/fts.h @@ -0,0 +1,146 @@ +/* $NetBSD: fts.h,v 1.14 2005/09/13 01:44:32 christos Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + * + * @(#)fts.h 8.3 (Berkeley) 8/14/94 + */ + +#ifndef _FTS_H_ +#define _FTS_H_ + +#ifndef __fts_stat_t +#define __fts_stat_t struct stat +#endif +#ifndef __fts_nlink_t +#define __fts_nlink_t nlink_t +#endif +#ifndef __fts_ino_t +#define __fts_ino_t ino_t +#endif + +typedef struct { + struct _ftsent *fts_cur; /* current node */ + struct _ftsent *fts_child; /* linked list of children */ + struct _ftsent **fts_array; /* sort array */ + dev_t fts_dev; /* starting device # */ + char *fts_path; /* path for this descent */ + int fts_rfd; /* fd for root */ + u_int fts_pathlen; /* sizeof(path) */ + u_int fts_nitems; /* elements in the sort array */ + int (*fts_compar) /* compare function */ + (const struct _ftsent **, const struct _ftsent **); + +#define FTS_COMFOLLOW 0x001 /* follow command line symlinks */ +#define FTS_LOGICAL 0x002 /* logical walk */ +#define FTS_NOCHDIR 0x004 /* don't change directories */ +#define FTS_NOSTAT 0x008 /* don't get stat info */ +#define FTS_PHYSICAL 0x010 /* physical walk */ +#define FTS_SEEDOT 0x020 /* return dot and dot-dot */ +#define FTS_XDEV 0x040 /* don't cross devices */ +#define FTS_WHITEOUT 0x080 /* return whiteout information */ +#define FTS_OPTIONMASK 0x0ff /* valid user option mask */ + +#define FTS_NAMEONLY 0x100 /* (private) child names only */ +#define FTS_STOP 0x200 /* (private) unrecoverable error */ + int fts_options; /* fts_open options, global flags */ +} FTS; + +typedef struct _ftsent { + struct _ftsent *fts_cycle; /* cycle node */ + struct _ftsent *fts_parent; /* parent directory */ + struct _ftsent *fts_link; /* next file in directory */ + long fts_number; /* local numeric value */ + void *fts_pointer; /* local address value */ + char *fts_accpath; /* access path */ + char *fts_path; /* root path */ + int fts_errno; /* errno for this node */ + int fts_symfd; /* fd for symlink */ + u_short fts_pathlen; /* strlen(fts_path) */ + u_short fts_namelen; /* strlen(fts_name) */ + + __fts_ino_t fts_ino; /* inode */ + dev_t fts_dev; /* device */ + __fts_nlink_t fts_nlink; /* link count */ + +#define FTS_ROOTPARENTLEVEL -1 +#define FTS_ROOTLEVEL 0 + short fts_level; /* depth (-1 to N) */ + +#define FTS_D 1 /* preorder directory */ +#define FTS_DC 2 /* directory that causes cycles */ +#define FTS_DEFAULT 3 /* none of the above */ +#define FTS_DNR 4 /* unreadable directory */ +#define FTS_DOT 5 /* dot or dot-dot */ +#define FTS_DP 6 /* postorder directory */ +#define FTS_ERR 7 /* error; errno is set */ +#define FTS_F 8 /* regular file */ +#define FTS_INIT 9 /* initialized only */ +#define FTS_NS 10 /* stat(2) failed */ +#define FTS_NSOK 11 /* no stat(2) requested */ +#define FTS_SL 12 /* symbolic link */ +#define FTS_SLNONE 13 /* symbolic link without target */ +#define FTS_W 14 /* whiteout object */ + u_short fts_info; /* user flags for FTSENT structure */ + +#define FTS_DONTCHDIR 0x01 /* don't chdir .. to the parent */ +#define FTS_SYMFOLLOW 0x02 /* followed a symlink to get here */ +#define FTS_ISW 0x04 /* this is a whiteout object */ + u_short fts_flags; /* private flags for FTSENT structure */ + +#define FTS_AGAIN 1 /* read node again */ +#define FTS_FOLLOW 2 /* follow symbolic link */ +#define FTS_NOINSTR 3 /* no instructions */ +#define FTS_SKIP 4 /* discard node */ + u_short fts_instr; /* fts_set() instructions */ + + __fts_stat_t *fts_statp; /* stat(2) information */ + char fts_name[1]; /* file name */ +} FTSENT; + +#include <sys/cdefs.h> + +#define __RENAME(n) +#define fts_children rtems_shell_fts_children +#define fts_close rtems_shell_fts_close +#define fts_open rtems_shell_fts_open +#define fts_read rtems_shell_fts_read +#define fts_set rtems_shell_fts_set + +__BEGIN_DECLS +#ifndef __LIBC12_SOURCE__ +FTSENT *fts_children(FTS *, int) __RENAME(__fts_children30); +int fts_close(FTS *) __RENAME(__fts_close30); +FTS *fts_open(char * const *, int, + int (*)(const FTSENT **, const FTSENT **)) __RENAME(__fts_open30); +FTSENT *fts_read(FTS *) __RENAME(__fts_read30); +int fts_set(FTS *, FTSENT *, int) __RENAME(__fts_set30); +#endif +__END_DECLS + +#endif /* !_FTS_H_ */ diff --git a/cpukit/libmisc/shell/hexdump-conv.c b/cpukit/libmisc/shell/hexdump-conv.c new file mode 100644 index 0000000000..44390baa32 --- /dev/null +++ b/cpukit/libmisc/shell/hexdump-conv.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef lint +static const char sccsid[] = "@(#)conv.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/hexdump/conv.c,v 1.9 2006/07/31 14:17:04 jkoshy Exp $"); + +#include <sys/types.h> + +#include <assert.h> +#include <ctype.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#include <wctype.h> +#include "hexdump.h" + +void +conv_c(rtems_shell_hexdump_globals* globals, PR *pr, u_char *p, size_t bufsize) +{ + char buf[10]; + char const *str; + wchar_t wc; + size_t clen, oclen; + int converr, pad, width; + char peekbuf[MB_LEN_MAX]; + + if (pr->mbleft > 0) { + str = "**"; + pr->mbleft--; + goto strpr; + } + + switch(*p) { + case '\0': + str = "\\0"; + goto strpr; + /* case '\a': */ + case '\007': + str = "\\a"; + goto strpr; + case '\b': + str = "\\b"; + goto strpr; + case '\f': + str = "\\f"; + goto strpr; + case '\n': + str = "\\n"; + goto strpr; + case '\r': + str = "\\r"; + goto strpr; + case '\t': + str = "\\t"; + goto strpr; + case '\v': + str = "\\v"; + goto strpr; + default: + break; + } + /* + * Multibyte characters are disabled for hexdump(1) for backwards + * compatibility and consistency (none of its other output formats + * recognize them correctly). + */ + converr = 0; + if (odmode && MB_CUR_MAX > 1) { + oclen = 0; +retry: + clen = mbrtowc(&wc, (char*)p, bufsize, &pr->mbstate); + if (clen == 0) + clen = 1; + else if (clen == (size_t)-1 || (clen == (size_t)-2 && + buf == peekbuf)) { + memset(&pr->mbstate, 0, sizeof(pr->mbstate)); + wc = *p; + clen = 1; + converr = 1; + } else if (clen == (size_t)-2) { + /* + * Incomplete character; peek ahead and see if we + * can complete it. + */ + oclen = bufsize; + bufsize = peek(globals, p = (u_char*)peekbuf, MB_CUR_MAX); + goto retry; + } + clen += oclen; + } else { + wc = *p; + clen = 1; + } + if (!converr && iswprint(wc)) { + if (!odmode) { + *pr->cchar = 'c'; + (void)printf(pr->fmt, (int)wc); + } else { + *pr->cchar = 'C'; + assert(strcmp(pr->fmt, "%3C") == 0); + width = wcwidth(wc); + assert(width >= 0); + pad = 3 - width; + if (pad < 0) + pad = 0; + (void)printf("%*s%lc", pad, "", wc); + pr->mbleft = clen - 1; + } + } else { + (void)sprintf(buf, "%03o", (int)*p); + str = buf; +strpr: *pr->cchar = 's'; + (void)printf(pr->fmt, str); + } +} + +void +conv_u(rtems_shell_hexdump_globals* globals, PR *pr, u_char *p) +{ + static char const * list[] = { + "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", + "bs", "ht", "lf", "vt", "ff", "cr", "so", "si", + "dle", "dcl", "dc2", "dc3", "dc4", "nak", "syn", "etb", + "can", "em", "sub", "esc", "fs", "gs", "rs", "us", + }; + + /* od used nl, not lf */ + if (*p <= 0x1f) { + *pr->cchar = 's'; + if (odmode && *p == 0x0a) + (void)printf(pr->fmt, "nl"); + else + (void)printf(pr->fmt, list[*p]); + } else if (*p == 0x7f) { + *pr->cchar = 's'; + (void)printf(pr->fmt, "del"); + } else if (odmode && *p == 0x20) { /* od replaced space with sp */ + *pr->cchar = 's'; + (void)printf(pr->fmt, " sp"); + } else if (isprint(*p)) { + *pr->cchar = 'c'; + (void)printf(pr->fmt, *p); + } else { + *pr->cchar = 'x'; + (void)printf(pr->fmt, (int)*p); + } +} diff --git a/cpukit/libmisc/shell/hexdump-display.c b/cpukit/libmisc/shell/hexdump-display.c new file mode 100644 index 0000000000..3e33fba143 --- /dev/null +++ b/cpukit/libmisc/shell/hexdump-display.c @@ -0,0 +1,431 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)display.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* not lint */ +#if 0 +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/hexdump/display.c,v 1.22 2004/08/04 02:47:32 tjr Exp $"); +#endif + +#include <sys/param.h> +#include <sys/stat.h> + +#include <stdint.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "hexdump.h" + +#define bcmp(s1,s2,sz) memcmp(s1,s2,sz) +#define bcopy(s,d,sz) memcpy(d,s,sz) +#define bzero(s,sz) memset(s,0,sz) +#define index(s,c) strchr(s,c) + +#if RTEMS_REMOVED +enum _vflag vflag = FIRST; + +static off_t address; /* address/offset in stream */ +static off_t eaddress; /* end address */ +#endif + +static void print(rtems_shell_hexdump_globals*, PR *, u_char *); + +void +display(rtems_shell_hexdump_globals* globals) +{ + FS *fs; + FU *fu; + PR *pr; + int cnt; + u_char *bp; + off_t saveaddress; + u_char savech, *savebp; + + savech = 0; + while ((bp = get(globals))) + for (fs = fshead, savebp = bp, saveaddress = address; fs; + fs = fs->nextfs, bp = savebp, address = saveaddress) + for (fu = fs->nextfu; fu; fu = fu->nextfu) { + if (fu->flags&F_IGNORE) + break; + for (cnt = fu->reps; cnt; --cnt) + for (pr = fu->nextpr; pr; address += pr->bcnt, + bp += pr->bcnt, pr = pr->nextpr) { + if (eaddress && address >= eaddress && + !(pr->flags & (F_TEXT|F_BPAD))) + bpad(pr); + if (cnt == 1 && pr->nospace) { + savech = *pr->nospace; + *pr->nospace = '\0'; + } + print(globals, pr, bp); + if (cnt == 1 && pr->nospace) + *pr->nospace = savech; + } + } + if (endfu) { + /* + * If eaddress not set, error or file size was multiple of + * blocksize, and no partial block ever found. + */ + if (!eaddress) { + if (!address) + return; + eaddress = address; + } + for (pr = endfu->nextpr; pr; pr = pr->nextpr) + switch(pr->flags) { + case F_ADDRESS: + (void)printf(pr->fmt, (quad_t)eaddress); + break; + case F_TEXT: + (void)printf("%s", pr->fmt); + break; + } + } +} + +static void +print(rtems_shell_hexdump_globals* globals, PR *pr, u_char *bp) +{ + long double ldbl; + double f8; + float f4; + int16_t s2; + int8_t s8; + int32_t s4; + u_int16_t u2; + u_int32_t u4; + u_int64_t u8; + + switch(pr->flags) { + case F_ADDRESS: + (void)printf(pr->fmt, (quad_t)address); + break; + case F_BPAD: + (void)printf(pr->fmt, ""); + break; + case F_C: + conv_c(globals, pr, bp, eaddress ? eaddress - address : + blocksize - address % blocksize); + break; + case F_CHAR: + (void)printf(pr->fmt, *bp); + break; + case F_DBL: + switch(pr->bcnt) { + case 4: + bcopy(bp, &f4, sizeof(f4)); + (void)printf(pr->fmt, f4); + break; + case 8: + bcopy(bp, &f8, sizeof(f8)); + (void)printf(pr->fmt, f8); + break; + default: + if (pr->bcnt == sizeof(long double)) { + bcopy(bp, &ldbl, sizeof(ldbl)); + (void)printf(pr->fmt, ldbl); + } + break; + } + break; + case F_INT: + switch(pr->bcnt) { + case 1: + (void)printf(pr->fmt, (quad_t)(signed char)*bp); + break; + case 2: + bcopy(bp, &s2, sizeof(s2)); + (void)printf(pr->fmt, (quad_t)s2); + break; + case 4: + bcopy(bp, &s4, sizeof(s4)); + (void)printf(pr->fmt, (quad_t)s4); + break; + case 8: + bcopy(bp, &s8, sizeof(s8)); + (void)printf(pr->fmt, s8); + break; + } + break; + case F_P: + (void)printf(pr->fmt, isprint(*bp) ? *bp : '.'); + break; + case F_STR: + (void)printf(pr->fmt, (char *)bp); + break; + case F_TEXT: + (void)printf("%s", pr->fmt); + break; + case F_U: + conv_u(globals, pr, bp); + break; + case F_UINT: + switch(pr->bcnt) { + case 1: + (void)printf(pr->fmt, (u_quad_t)*bp); + break; + case 2: + bcopy(bp, &u2, sizeof(u2)); + (void)printf(pr->fmt, (u_quad_t)u2); + break; + case 4: + bcopy(bp, &u4, sizeof(u4)); + (void)printf(pr->fmt, (u_quad_t)u4); + break; + case 8: + bcopy(bp, &u8, sizeof(u8)); + (void)printf(pr->fmt, u8); + break; + } + break; + } +} + +void +bpad(PR *pr) +{ + static char const *spec = " -0+#"; + char *p1, *p2; + + /* + * Remove all conversion flags; '-' is the only one valid + * with %s, and it's not useful here. + */ + pr->flags = F_BPAD; + pr->cchar[0] = 's'; + pr->cchar[1] = '\0'; + for (p1 = pr->fmt; *p1 != '%'; ++p1); + for (p2 = ++p1; *p1 && index(spec, *p1); ++p1); + while ((*p2++ = *p1++)); +} + +static char **_argv; + +u_char * +get(rtems_shell_hexdump_globals* globals) +{ +#if RTEMS_REMOVED + static int ateof = 1; + static u_char *curp, *savp; +#endif + int n; + int need, nread; + int valid_save = 0; + u_char *tmpp; + + if (!curp) { + if ((curp = calloc(1, blocksize)) == NULL) + err(exit_jump, 1, NULL); + if ((savp = calloc(1, blocksize)) == NULL) + err(exit_jump, 1, NULL); + } else { + tmpp = curp; + curp = savp; + savp = tmpp; + address += blocksize; + valid_save = 1; + } + for (need = blocksize, nread = 0;;) { + /* + * if read the right number of bytes, or at EOF for one file, + * and no other files are available, zero-pad the rest of the + * block and set the end flag. + */ + if (!length || (ateof && !next(globals, (char **)NULL))) { + if (odmode && address < skip) + errx(exit_jump, 1, "cannot skip past end of input"); + if (need == blocksize) + return((u_char *)NULL); + /* + * XXX bcmp() is not quite right in the presence + * of multibyte characters. + */ + if (vflag != ALL && + valid_save && + bcmp(curp, savp, nread) == 0) { + if (vflag != DUP) + (void)printf("*\n"); + return((u_char *)NULL); + } + bzero((char *)curp + nread, need); + eaddress = address + nread; + return(curp); + } + n = fread((char *)curp + nread, sizeof(u_char), + length == -1 ? need : MIN(length, need), hdstdin); + if (!n) { + if (ferror(hdstdin)) + warn("%s", _argv[-1]); + ateof = 1; + continue; + } + ateof = 0; + if (length != -1) + length -= n; + if (!(need -= n)) { + /* + * XXX bcmp() is not quite right in the presence + * of multibyte characters. + */ + if (vflag == ALL || vflag == FIRST || + valid_save == 0 || + bcmp(curp, savp, blocksize) != 0) { + if (vflag == DUP || vflag == FIRST) + vflag = WAIT; + return(curp); + } + if (vflag == WAIT) + (void)printf("*\n"); + vflag = DUP; + address += blocksize; + need = blocksize; + nread = 0; + } + else + nread += n; + } +} + +size_t +peek(rtems_shell_hexdump_globals* globals, u_char *buf, size_t nbytes) +{ + size_t n, nread; + int c; + + if (length != -1 && nbytes > (unsigned int)length) + nbytes = length; + nread = 0; + while (nread < nbytes && (c = getchar()) != EOF) { + *buf++ = c; + nread++; + } + n = nread; + while (n-- > 0) { + c = *--buf; + ungetc(c, hdstdin); + } + return (nread); +} + +int +next(rtems_shell_hexdump_globals* globals, char **argv) +{ +#if RTEMS_REMOVED + static int done; +#endif + int statok; + + if (argv) { + _argv = argv; + return(1); + } + for (;;) { + if (*_argv) { + done = 1; + if (!hdstdin) { + hdstdin = malloc(sizeof(FILE)); + if (!hdstdin) + { + errno = ENOMEM; + err(exit_jump, 1, "file name allocation"); + } + memset (hdstdin, 0, sizeof(FILE)); + } + if (!(hdstdin = freopen(*_argv, "r", hdstdin))) { + warn("%s", *_argv); + exitval = 1; + ++_argv; + continue; + } + statok = 1; + } else { + errno = ECANCELED; + err(exit_jump, 1, "no file (stdin no supported)"); + if (done++) + return(0); + statok = 0; + } + if (skip) + doskip(globals, statok ? *_argv : "stdin", statok); + if (*_argv) + ++_argv; + if (!skip) + return(1); + } + /* NOTREACHED */ +} + +void +doskip(rtems_shell_hexdump_globals* globals, const char *fname, int statok) +{ + int cnt; + struct stat sb; + + if (statok) { + if (fstat(fileno(hdstdin), &sb)) + err(exit_jump, 1, "%s", fname); + /* can seek block devices on RTEMS */ + if (0 && S_ISREG(sb.st_mode) && skip >= sb.st_size) { + address += sb.st_size; + skip -= sb.st_size; + return; + } + if (1 || S_ISREG(sb.st_mode)) { + if (fseeko(hdstdin, skip, SEEK_SET)) + err(exit_jump, 1, "%s", fname); + address += skip; + skip = 0; + } else { + for (cnt = 0; cnt < skip; ++cnt) + if (getchar() == EOF) + break; + address += cnt; + skip -= cnt; + } + } +} diff --git a/cpukit/libmisc/shell/hexdump-odsyntax.c b/cpukit/libmisc/shell/hexdump-odsyntax.c new file mode 100644 index 0000000000..24e8d59e73 --- /dev/null +++ b/cpukit/libmisc/shell/hexdump-odsyntax.c @@ -0,0 +1,451 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)odsyntax.c 8.2 (Berkeley) 5/4/95"; +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/hexdump/odsyntax.c,v 1.17 2004/07/22 13:14:42 johan Exp $"); +#endif + +#include <sys/types.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <float.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "hexdump.h" + +#define __need_getopt_newlib +#include <getopt.h> + +#define PADDING " " + +#if RTEMS_REMOVED +int odmode; +#endif + +static void odadd(rtems_shell_hexdump_globals*, const char *); +static void odformat(rtems_shell_hexdump_globals*, const char *); +static const char *odformatfp(rtems_shell_hexdump_globals*, char, const char *); +static const char *odformatint(rtems_shell_hexdump_globals*, char, const char *); +static void odoffset(rtems_shell_hexdump_globals*, int, char ***); +static void odusage(rtems_shell_hexdump_globals*); + +void +oldsyntax(rtems_shell_hexdump_globals* globals, int argc, char ***argvp) +{ + static char empty[] = "", padding[] = PADDING; + int ch; + char **argv, *end; + + struct getopt_data getopt_reent; + memset(&getopt_reent, 0, sizeof(getopt_data)); + + /* Add initial (default) address format. -A may change it later. */ +#define TYPE_OFFSET 7 + add(globals, "\"%07.7_Ao\n\""); + add(globals, "\"%07.7_ao \""); + + odmode = 1; + argv = *argvp; + while ((ch = getopt_r(argc, argv, "A:aBbcDdeFfHhIij:LlN:Oost:vXx", &getopt_reent)) != -1) + switch (ch) { + case 'A': + switch (*optarg) { + case 'd': case 'o': case 'x': + fshead->nextfu->fmt[TYPE_OFFSET] = *optarg; + fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = + *optarg; + break; + case 'n': + fshead->nextfu->fmt = empty; + fshead->nextfs->nextfu->fmt = padding; + break; + default: + errx(exit_jump, 1, "%s: invalid address base", optarg); + } + break; + case 'a': + odformat(globals, "a"); + break; + case 'B': + case 'o': + odformat(globals, "o2"); + break; + case 'b': + odformat(globals, "o1"); + break; + case 'c': + odformat(globals, "c"); + break; + case 'd': + odformat(globals, "u2"); + break; + case 'D': + odformat(globals, "u4"); + break; + case 'e': /* undocumented in od */ + case 'F': + odformat(globals, "fD"); + break; + case 'f': + odformat(globals, "fF"); + break; + case 'H': + case 'X': + odformat(globals, "x4"); + break; + case 'h': + case 'x': + odformat(globals, "x2"); + break; + case 'I': + case 'L': + case 'l': + odformat(globals, "dL"); + break; + case 'i': + odformat(globals, "dI"); + break; + case 'j': + errno = 0; + skip = strtoll(optarg, &end, 0); + if (*end == 'b') + skip *= 512; + else if (*end == 'k') + skip *= 1024; + else if (*end == 'm') + skip *= 1048576L; + if (errno != 0 || skip < 0 || strlen(end) > 1) + errx(exit_jump, 1, "%s: invalid skip amount", optarg); + break; + case 'N': + if ((length = atoi(optarg)) <= 0) + errx(exit_jump, 1, "%s: invalid length", optarg); + break; + case 'O': + odformat(globals, "o4"); + break; + case 's': + odformat(globals, "d2"); + break; + case 't': + odformat(globals, optarg); + break; + case 'v': + vflag = ALL; + break; + case '?': + default: + odusage(globals); + } + + if (fshead->nextfs->nextfs == NULL) + odformat(globals, "oS"); + + argc -= getopt_reent.optind; + *argvp += getopt_reent.optind; + + if (argc) + odoffset(globals, argc, argvp); +} + +static void +odusage(rtems_shell_hexdump_globals* globals) +{ + + fprintf(stderr, +"usage: od [-aBbcDdeFfHhIiLlOosvXx] [-A base] [-j skip] [-N length] [-t type]\n"); + fprintf(stderr, +" [[+]offset[.][Bb]] [file ...]\n"); + exit(1); +} + +static void +odoffset(rtems_shell_hexdump_globals* globals, int argc, char ***argvp) +{ + char *p, *num, *end; + int base; + + /* + * The offset syntax of od(1) was genuinely bizarre. First, if + * it started with a plus it had to be an offset. Otherwise, if + * there were at least two arguments, a number or lower-case 'x' + * followed by a number makes it an offset. By default it was + * octal; if it started with 'x' or '0x' it was hex. If it ended + * in a '.', it was decimal. If a 'b' or 'B' was appended, it + * multiplied the number by 512 or 1024 byte units. There was + * no way to assign a block count to a hex offset. + * + * We assume it's a file if the offset is bad. + */ + p = argc == 1 ? (*argvp)[0] : (*argvp)[1]; + + if (*p != '+' && (argc < 2 || + (!isdigit((unsigned char)p[0]) && (p[0] != 'x' || !isxdigit((unsigned char)p[1]))))) + return; + + base = 0; + /* + * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and + * set base. + */ + if (p[0] == '+') + ++p; + if (p[0] == 'x' && isxdigit((unsigned char)p[1])) { + ++p; + base = 16; + } else if (p[0] == '0' && p[1] == 'x') { + p += 2; + base = 16; + } + + /* skip over the number */ + if (base == 16) + for (num = p; isxdigit((unsigned char)*p); ++p); + else + for (num = p; isdigit((unsigned char)*p); ++p); + + /* check for no number */ + if (num == p) + return; + + /* if terminates with a '.', base is decimal */ + if (*p == '.') { + if (base) + return; + base = 10; + } + + skip = strtoll(num, &end, base ? base : 8); + + /* if end isn't the same as p, we got a non-octal digit */ + if (end != p) { + skip = 0; + return; + } + + if (*p) { + if (*p == 'B') { + skip *= 1024; + ++p; + } else if (*p == 'b') { + skip *= 512; + ++p; + } + } + + if (*p) { + skip = 0; + return; + } + + /* + * If the offset uses a non-octal base, the base of the offset + * is changed as well. This isn't pretty, but it's easy. + */ + if (base == 16) { + fshead->nextfu->fmt[TYPE_OFFSET] = 'x'; + fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'x'; + } else if (base == 10) { + fshead->nextfu->fmt[TYPE_OFFSET] = 'd'; + fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'd'; + } + + /* Terminate file list. */ + (*argvp)[1] = NULL; +} + +static void +odformat(rtems_shell_hexdump_globals* globals, const char *fmt) +{ + char fchar; + + while (*fmt != '\0') { + switch ((fchar = *fmt++)) { + case 'a': + odadd(globals, "16/1 \"%3_u \" \"\\n\""); + break; + case 'c': + odadd(globals, "16/1 \"%3_c \" \"\\n\""); + break; + case 'o': case 'u': case 'd': case 'x': + fmt = odformatint(globals, fchar, fmt); + break; + case 'f': + fmt = odformatfp(globals, fchar, fmt); + break; + default: + errx(exit_jump, 1, "%c: unrecognised format character", fchar); + } + } +} +#define __unused + +static const char * +odformatfp(rtems_shell_hexdump_globals* globals, char fchar __unused, const char *fmt) +{ + size_t isize; + int digits; + char *end, *hdfmt; + + isize = sizeof(double); + switch (*fmt) { + case 'F': + isize = sizeof(float); + fmt++; + break; + case 'D': + isize = sizeof(double); + fmt++; + break; + case 'L': + isize = sizeof(long double); + fmt++; + break; + default: + if (isdigit((unsigned char)*fmt)) { + errno = 0; + isize = (size_t)strtoul(fmt, &end, 10); + if (errno != 0 || isize == 0) + errx(exit_jump, 1, "%s: invalid size", fmt); + fmt = (const char *)end; + } + } + if (isize == sizeof(float) ) { + digits = FLT_DIG; + } else if (isize == sizeof(double)) { + digits = DBL_DIG; + } else if (isize == sizeof(long double)) { + digits = LDBL_DIG; + } else { + errx(exit_jump, 1, "unsupported floating point size %zu", + isize); + } + + asprintf(&hdfmt, "%lu/%lu \" %%%d.%de \" \"\\n\"", + 16UL / (u_long)isize, (u_long)isize, digits + 8, digits); + if (hdfmt == NULL) + err(exit_jump, 1, NULL); + odadd(globals, hdfmt); + free(hdfmt); + + return (fmt); +} + +static const char * +odformatint(rtems_shell_hexdump_globals* globals, char fchar, const char *fmt) +{ + unsigned long long n; + size_t isize; + int digits; + char *end, *hdfmt; + + isize = sizeof(int); + switch (*fmt) { + case 'C': + isize = sizeof(char); + fmt++; + break; + case 'I': + isize = sizeof(int); + fmt++; + break; + case 'L': + isize = sizeof(long); + fmt++; + break; + case 'S': + isize = sizeof(short); + fmt++; + break; + default: + if (isdigit((unsigned char)*fmt)) { + errno = 0; + isize = (size_t)strtoul(fmt, &end, 10); + if (errno != 0 || isize == 0) + errx(exit_jump, 1, "%s: invalid size", fmt); + if (isize != sizeof(char) && isize != sizeof(short) && + isize != sizeof(int) && isize != sizeof(long)) + errx(exit_jump, 1, "unsupported int size %lu", + (u_long)isize); + fmt = (const char *)end; + } + } + + /* + * Calculate the maximum number of digits we need to + * fit the number. Overestimate for decimal with log + * base 8. We need one extra space for signed numbers + * to store the sign. + */ + n = (1ULL << (8 * isize)) - 1; + digits = 0; + while (n != 0) { + digits++; + n >>= (fchar == 'x') ? 4 : 3; + } + if (fchar == 'd') + digits++; + asprintf(&hdfmt, "%lu/%lu \"%*s%%%s%d%c\" \"\\n\"", + 16UL / (u_long)isize, (u_long)isize, (int)(4 * isize - digits), + "", (fchar == 'd' || fchar == 'u') ? "" : "0", digits, fchar); + if (hdfmt == NULL) + err(exit_jump, 1, NULL); + odadd(globals, hdfmt); + free(hdfmt); + + return (fmt); +} + +static void +odadd(rtems_shell_hexdump_globals* globals, const char *fmt) +{ + static int needpad; + + if (needpad) + add(globals, "\""PADDING"\""); + add(globals, fmt); + needpad = 1; +} diff --git a/cpukit/libmisc/shell/hexdump-parse.c b/cpukit/libmisc/shell/hexdump-parse.c new file mode 100644 index 0000000000..1c35ba4649 --- /dev/null +++ b/cpukit/libmisc/shell/hexdump-parse.c @@ -0,0 +1,531 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/6/93"; +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/hexdump/parse.c,v 1.14 2006/08/09 19:12:10 maxim Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> + +#include <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> + +#define index(s,c) strchr(s,c) + +#include "hexdump.h" + +#if RTEMS_REMOVED +FU *endfu; /* format at end-of-data */ +#endif + +void +addfile(rtems_shell_hexdump_globals* globals, char *name) +{ + unsigned char *p; + FILE *fp; + int ch; + char buf[2048 + 1]; + + if ((fp = fopen(name, "r")) == NULL) + err(exit_jump, 1, "%s", name); + while (fgets(buf, sizeof(buf), fp)) { + if (!(p = (unsigned char*)index(buf, '\n'))) { + warnx("line too long"); + while ((ch = getchar()) != '\n' && ch != EOF); + continue; + } + *p = '\0'; + for (p = (unsigned char*) buf; *p && isspace(*p); ++p); + if (!*p || *p == '#') + continue; + add(globals, (char*)p); + } + (void)fclose(fp); +} + +void +add(rtems_shell_hexdump_globals* globals, const char *fmt) +{ + unsigned const char *p, *savep; + static FS **nextfs; + FS *tfs; + FU *tfu, **nextfu; + + /* start new linked list of format units */ + if ((tfs = calloc(1, sizeof(FS))) == NULL) + err(exit_jump, 1, NULL); + if (!fshead) + fshead = tfs; + else + *nextfs = tfs; + nextfs = &tfs->nextfs; + nextfu = &tfs->nextfu; + + /* take the format string and break it up into format units */ + for (p = (unsigned const char*)fmt;;) { + /* skip leading white space */ + for (; isspace(*p); ++p); + if (!*p) + break; + + /* allocate a new format unit and link it in */ + if ((tfu = calloc(1, sizeof(FU))) == NULL) + err(exit_jump, 1, NULL); + *nextfu = tfu; + nextfu = &tfu->nextfu; + tfu->reps = 1; + + /* if leading digit, repetition count */ + if (isdigit(*p)) { + for (savep = p; isdigit(*p); ++p); + if (!isspace(*p) && *p != '/') + badfmt(globals, fmt); + /* may overwrite either white space or slash */ + tfu->reps = atoi((char*)savep); + tfu->flags = F_SETREP; + /* skip trailing white space */ + for (++p; isspace(*p); ++p); + } + + /* skip slash and trailing white space */ + if (*p == '/') + while (isspace(*++p)); + + /* byte count */ + if (isdigit(*p)) { + for (savep = p; isdigit(*p); ++p); + if (!isspace(*p)) + badfmt(globals, fmt); + tfu->bcnt = atoi((char*)savep); + /* skip trailing white space */ + for (++p; isspace(*p); ++p); + } + + /* format */ + if (*p != '"') + badfmt(globals, fmt); + for (savep = ++p; *p != '"';) + if (*p++ == 0) + badfmt(globals, fmt); + if (!(tfu->fmt = malloc(p - savep + 1))) + err(exit_jump, 1, NULL); + (void) strncpy(tfu->fmt, (char*)savep, p - savep); + tfu->fmt[p - savep] = '\0'; + escape(tfu->fmt); + p++; + } +} + +static const char *spec = ".#-+ 0123456789"; + +int +size(rtems_shell_hexdump_globals* globals, FS *fs) +{ + FU *fu; + int bcnt, cursize; + unsigned char *fmt; + int prec; + + /* figure out the data block size needed for each format unit */ + for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) { + if (fu->bcnt) { + cursize += fu->bcnt * fu->reps; + continue; + } + for (bcnt = prec = 0, fmt = (unsigned char*) fu->fmt; *fmt; ++fmt) { + if (*fmt != '%') + continue; + /* + * skip any special chars -- save precision in + * case it's a %s format. + */ + while (index(spec + 1, *++fmt)); + if (*fmt == '.' && isdigit(*++fmt)) { + prec = atoi((char*)fmt); + while (isdigit(*++fmt)); + } + switch(*fmt) { + case 'c': + bcnt += 1; + break; + case 'd': case 'i': case 'o': case 'u': + case 'x': case 'X': + bcnt += 4; + break; + case 'e': case 'E': case 'f': case 'g': case 'G': + bcnt += 8; + break; + case 's': + bcnt += prec; + break; + case '_': + switch(*++fmt) { + case 'c': case 'p': case 'u': + bcnt += 1; + break; + } + } + } + cursize += bcnt * fu->reps; + } + return (cursize); +} + +void +rewrite(rtems_shell_hexdump_globals* globals, FS *fs) +{ + enum { NOTOKAY, USEBCNT, USEPREC } sokay; + PR *pr, **nextpr; + FU *fu; + unsigned char *p1, *p2, *fmtp; + char savech, cs[3]; + int nconv, prec; + size_t len; + + pr = NULL; + nextpr = NULL; + prec = 0; + + for (fu = fs->nextfu; fu; fu = fu->nextfu) { + /* + * Break each format unit into print units; each conversion + * character gets its own. + */ + for (nconv = 0, fmtp = (unsigned char*)fu->fmt; *fmtp; nextpr = &pr->nextpr) { + if ((pr = calloc(1, sizeof(PR))) == NULL) + err(exit_jump, 1, NULL); + if (!fu->nextpr) + fu->nextpr = pr; + else { + if (nextpr) + *nextpr = pr; + } + + /* Skip preceding text and up to the next % sign. */ + for (p1 = fmtp; *p1 && *p1 != '%'; ++p1); + + /* Only text in the string. */ + if (!*p1) { + pr->fmt = (char*)fmtp; + pr->flags = F_TEXT; + break; + } + + /* + * Get precision for %s -- if have a byte count, don't + * need it. + */ + if (fu->bcnt) { + sokay = USEBCNT; + /* Skip to conversion character. */ + for (++p1; index(spec, *p1); ++p1); + } else { + /* Skip any special chars, field width. */ + while (index(spec + 1, *++p1)); + if (*p1 == '.' && isdigit(*++p1)) { + sokay = USEPREC; + prec = atoi((char*)p1); + while (isdigit(*++p1)); + } else + sokay = NOTOKAY; + } + + p2 = p1 + 1; /* Set end pointer. */ + cs[0] = *p1; /* Set conversion string. */ + cs[1] = '\0'; + + /* + * Figure out the byte count for each conversion; + * rewrite the format as necessary, set up blank- + * padding for end of data. + */ + switch(cs[0]) { + case 'c': + pr->flags = F_CHAR; + switch(fu->bcnt) { + case 0: case 1: + pr->bcnt = 1; + break; + default: + p1[1] = '\0'; + badcnt(globals, (char*)p1); + } + break; + case 'd': case 'i': + pr->flags = F_INT; + goto isint; + case 'o': case 'u': case 'x': case 'X': + pr->flags = F_UINT; +isint: cs[2] = '\0'; + cs[1] = cs[0]; + cs[0] = 'q'; + switch(fu->bcnt) { + case 0: case 4: + pr->bcnt = 4; + break; + case 1: + pr->bcnt = 1; + break; + case 2: + pr->bcnt = 2; + break; + default: + p1[1] = '\0'; + badcnt(globals, (char*)p1); + } + break; + case 'e': case 'E': case 'f': case 'g': case 'G': + pr->flags = F_DBL; + switch(fu->bcnt) { + case 0: case 8: + pr->bcnt = 8; + break; + case 4: + pr->bcnt = 4; + break; + default: + if (fu->bcnt == sizeof(long double)) { + cs[2] = '\0'; + cs[1] = cs[0]; + cs[0] = 'L'; + pr->bcnt = sizeof(long double); + } else { + p1[1] = '\0'; + badcnt(globals, (char*)p1); + } + } + break; + case 's': + pr->flags = F_STR; + switch(sokay) { + case NOTOKAY: + badsfmt(globals); + case USEBCNT: + pr->bcnt = fu->bcnt; + break; + case USEPREC: + pr->bcnt = prec; + break; + } + break; + case '_': + ++p2; + switch(p1[1]) { + case 'A': + endfu = fu; + fu->flags |= F_IGNORE; + /* FALLTHROUGH */ + case 'a': + pr->flags = F_ADDRESS; + ++p2; + switch(p1[2]) { + case 'd': case 'o': case'x': + cs[0] = 'q'; + cs[1] = p1[2]; + cs[2] = '\0'; + break; + default: + p1[3] = '\0'; + badconv(globals, (char*)p1); + } + break; + case 'c': + pr->flags = F_C; + /* cs[0] = 'c'; set in conv_c */ + goto isint2; + case 'p': + pr->flags = F_P; + cs[0] = 'c'; + goto isint2; + case 'u': + pr->flags = F_U; + /* cs[0] = 'c'; set in conv_u */ +isint2: switch(fu->bcnt) { + case 0: case 1: + pr->bcnt = 1; + break; + default: + p1[2] = '\0'; + badcnt(globals, (char*)p1); + } + break; + default: + p1[2] = '\0'; + badconv(globals, (char*)p1); + } + break; + default: + p1[1] = '\0'; + badconv(globals, (char*)p1); + } + + /* + * Copy to PR format string, set conversion character + * pointer, update original. + */ + savech = *p2; + p1[0] = '\0'; + len = strlen((char*)fmtp) + strlen(cs) + 1; + if ((pr->fmt = calloc(1, len)) == NULL) + err(exit_jump, 1, NULL); + snprintf(pr->fmt, len, "%s%s", fmtp, cs); + *p2 = savech; + pr->cchar = pr->fmt + (p1 - fmtp); + fmtp = p2; + + /* Only one conversion character if byte count. */ + if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++) + errx(exit_jump, 1, "byte count with multiple conversion characters"); + } + /* + * If format unit byte count not specified, figure it out + * so can adjust rep count later. + */ + if (!fu->bcnt) + for (pr = fu->nextpr; pr; pr = pr->nextpr) + fu->bcnt += pr->bcnt; + } + if (pr) { + free(pr); + pr = NULL; + } + /* + * If the format string interprets any data at all, and it's + * not the same as the blocksize, and its last format unit + * interprets any data at all, and has no iteration count, + * repeat it as necessary. + * + * If, rep count is greater than 1, no trailing whitespace + * gets output from the last iteration of the format unit. + */ + for (fu = fs->nextfu; fu; fu = fu->nextfu) { + if (!fu->nextfu && fs->bcnt < blocksize && + !(fu->flags&F_SETREP) && fu->bcnt) + fu->reps += (blocksize - fs->bcnt) / fu->bcnt; + if (fu->reps > 1) { + for (pr = fu->nextpr;; pr = pr->nextpr) + if (!pr->nextpr) + break; + for (p1 = (unsigned char*)pr->fmt, p2 = NULL; *p1; ++p1) + p2 = isspace(*p1) ? p1 : NULL; + if (p2) + pr->nospace = (char*)p2; + } + } +#ifdef DEBUG + for (fu = fs->nextfu; fu; fu = fu->nextfu) { + (void)printf("fmt:"); + for (pr = fu->nextpr; pr; pr = pr->nextpr) + (void)printf(" {%s}", pr->fmt); + (void)printf("\n"); + } +#endif +} + +void +escape(char *p1) +{ + char *p2; + + /* alphabetic escape sequences have to be done in place */ + for (p2 = p1;; ++p1, ++p2) { + if (!*p1) { + *p2 = *p1; + break; + } + if (*p1 == '\\') + switch(*++p1) { + case 'a': + /* *p2 = '\a'; */ + *p2 = '\007'; + break; + case 'b': + *p2 = '\b'; + break; + case 'f': + *p2 = '\f'; + break; + case 'n': + *p2 = '\n'; + break; + case 'r': + *p2 = '\r'; + break; + case 't': + *p2 = '\t'; + break; + case 'v': + *p2 = '\v'; + break; + default: + *p2 = *p1; + break; + } + } +} + +void +badcnt(rtems_shell_hexdump_globals* globals, char *s) +{ + errx(exit_jump, 1, "%s: bad byte count", s); +} + +void +badsfmt(rtems_shell_hexdump_globals* globals) +{ + errx(exit_jump, 1, "%%s: requires a precision or a byte count"); +} + +void +badfmt(rtems_shell_hexdump_globals* globals, const char *fmt) +{ + errx(exit_jump, 1, "\"%s\": bad format", fmt); +} + +void +badconv(rtems_shell_hexdump_globals* globals, char *ch) +{ + errx(exit_jump, 1, "%%%s: bad conversion character", ch); +} diff --git a/cpukit/libmisc/shell/hexdump.h b/cpukit/libmisc/shell/hexdump.h new file mode 100644 index 0000000000..5b0e61b966 --- /dev/null +++ b/cpukit/libmisc/shell/hexdump.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)hexdump.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: src/usr.bin/hexdump/hexdump.h,v 1.9 2004/07/11 01:11:12 tjr Exp $ + */ + +#include <wchar.h> + +typedef struct _pr { + struct _pr *nextpr; /* next print unit */ +#define F_ADDRESS 0x001 /* print offset */ +#define F_BPAD 0x002 /* blank pad */ +#define F_C 0x004 /* %_c */ +#define F_CHAR 0x008 /* %c */ +#define F_DBL 0x010 /* %[EefGf] */ +#define F_INT 0x020 /* %[di] */ +#define F_P 0x040 /* %_p */ +#define F_STR 0x080 /* %s */ +#define F_U 0x100 /* %_u */ +#define F_UINT 0x200 /* %[ouXx] */ +#define F_TEXT 0x400 /* no conversions */ + u_int flags; /* flag values */ + int bcnt; /* byte count */ + char *cchar; /* conversion character */ + char *fmt; /* printf format */ + char *nospace; /* no whitespace version */ + int mbleft; /* bytes left of multibyte char. */ + mbstate_t mbstate; /* conversion state */ +} PR; + +typedef struct _fu { + struct _fu *nextfu; /* next format unit */ + struct _pr *nextpr; /* next print unit */ +#define F_IGNORE 0x01 /* %_A */ +#define F_SETREP 0x02 /* rep count set, not default */ + u_int flags; /* flag values */ + int reps; /* repetition count */ + int bcnt; /* byte count */ + char *fmt; /* format string */ +} FU; + +typedef struct _fs { /* format strings */ + struct _fs *nextfs; /* linked list of format strings */ + struct _fu *nextfu; /* linked list of format units */ + int bcnt; +} FS; + +#if 0 +extern FS *fshead; /* head of format strings list */ +extern FU *endfu; /* format at end-of-data */ +extern int blocksize; /* data block size */ +extern int exitval; /* final exit value */ +extern int odmode; /* are we acting as od(1)? */ +extern int length; /* amount of data to read */ +extern off_t skip; /* amount of data to skip at start */ +enum _vflag { ALL, DUP, FIRST, WAIT }; /* -v values */ +extern enum _vflag vflag; +#endif + +#include <setjmp.h> +#include <stdio.h> + +enum _vflag { ALL, DUP, FIRST, WAIT }; /* -v values */ +typedef struct rtems_shell_hexdump_globals_t { + FS *fshead; /* head of format strings list */ + FU *endfu; /* format at end-of-data */ + int blocksize; /* data block size */ + int exitval; /* final exit value */ + int odmode; /* are we acting as od(1)? */ + int length; /* amount of data to read */ + off_t skip; /* amount of data to skip at start */ + enum _vflag vflag; + + off_t address; + off_t eaddress; + int ateof; + u_char *curp; + u_char *savp; + int done; + + FILE* hdstdin; + + int exit_code; + jmp_buf exit_jmp; +} rtems_shell_hexdump_globals; + +#define fshead globals->fshead +#define endfu globals->endfu +#define blocksize globals->blocksize +#define exitval globals->exitval +#define odmode globals->odmode +#define length globals->length +#define skip globals->skip +#define vflag globals->vflag + +#define address globals->address +#define eaddress globals->eaddress +#define ateof globals->ateof +#define curp globals->curp +#define savp globals->savp +#define done globals->done + +#define hdstdin globals->hdstdin + +#define exit_jump &(globals->exit_jmp) + +#define add rtems_shell_hexdump_add +#define addfile rtems_shell_hexdump_addfile +#define badcnt rtems_shell_hexdump_badcnt +#define badconv rtems_shell_hexdump_badconv +#define badfmt rtems_shell_hexdump_badfmt +#define badsfmt rtems_shell_hexdump_badsfmt +#define bpad rtems_shell_hexdump_bpad +#define conv_c rtems_shell_hexdump_conv_c +#define conv_u rtems_shell_hexdump_conv_u +#define display rtems_shell_hexdump_display +#define doskip rtems_shell_hexdump_doskip +#define escape rtems_shell_hexdump_escape +#define get rtems_shell_hexdump_get +#define newsyntax rtems_shell_hexdump_newsyntax +#define next rtems_shell_hexdump_next +#define nomem rtems_shell_hexdump_nomem +#define oldsyntax rtems_shell_hexdump_oldsyntax +#define peek rtems_shell_hexdump_peek +#define rewrite rtems_shell_hexdump_rewrite +#define size rtems_shell_hexdump_size +#define usage rtems_shell_hexdump_usage + +void add(rtems_shell_hexdump_globals*, const char *); +void addfile(rtems_shell_hexdump_globals*, char *); +void badcnt(rtems_shell_hexdump_globals*, char *); +void badconv(rtems_shell_hexdump_globals*, char *); +void badfmt(rtems_shell_hexdump_globals*, const char *); +void badsfmt(rtems_shell_hexdump_globals*); +void bpad(PR *); +void conv_c(rtems_shell_hexdump_globals*, PR *, u_char *, size_t); +void conv_u(rtems_shell_hexdump_globals*, PR *, u_char *); +void display(rtems_shell_hexdump_globals*); +void doskip(rtems_shell_hexdump_globals*, const char *, int); +void escape(char *); +u_char *get(rtems_shell_hexdump_globals*); +void newsyntax(rtems_shell_hexdump_globals*, int, char ***); +int next(rtems_shell_hexdump_globals*, char **); +void nomem(void); +void oldsyntax(rtems_shell_hexdump_globals*, int, char ***); +size_t peek(rtems_shell_hexdump_globals*, u_char *, size_t); +void rewrite(rtems_shell_hexdump_globals*, FS *); +int size(rtems_shell_hexdump_globals*, FS *); +void usage(rtems_shell_hexdump_globals*); + +#define exit(ec) rtems_shell_hexdump_exit(globals, ec) + +void rtems_shell_hexdump_exit(rtems_shell_hexdump_globals* globals, int code); diff --git a/cpukit/libmisc/shell/hexsyntax.c b/cpukit/libmisc/shell/hexsyntax.c new file mode 100644 index 0000000000..0f80738172 --- /dev/null +++ b/cpukit/libmisc/shell/hexsyntax.c @@ -0,0 +1,158 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)hexsyntax.c 8.2 (Berkeley) 5/4/95"; +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/hexdump/hexsyntax.c,v 1.12 2002/09/04 23:29:01 dwmalone Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define rindex(s,c) strrchr(s,c) + +#include "hexdump.h" + +#define __need_getopt_newlib +#include <getopt.h> + +#if RTEMS_REMOVED +off_t skip; /* bytes to skip */ +#endif + +void +newsyntax(rtems_shell_hexdump_globals* globals, int argc, char ***argvp) +{ + int ch; + char *p, **argv; + + struct getopt_data getopt_reent; + memset(&getopt_reent, 0, sizeof(getopt_data)); + + argv = *argvp; + if ((p = rindex(argv[0], 'h')) != NULL && + strcmp(p, "hd") == 0) { + /* "Canonical" format, implies -C. */ + add(globals, "\"%08.8_Ax\n\""); + add(globals, "\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \" "); + add(globals, "\" |\" 16/1 \"%_p\" \"|\\n\""); + } + while ((ch = getopt_r(argc, argv, "bcCde:f:n:os:vx", &getopt_reent)) != -1) + switch (ch) { + case 'b': + add(globals, "\"%07.7_Ax\n\""); + add(globals, "\"%07.7_ax \" 16/1 \"%03o \" \"\\n\""); + break; + case 'c': + add(globals, "\"%07.7_Ax\n\""); + add(globals, "\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\""); + break; + case 'C': + add(globals, "\"%08.8_Ax\n\""); + add(globals, "\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \" "); + add(globals, "\" |\" 16/1 \"%_p\" \"|\\n\""); + break; + case 'd': + add(globals, "\"%07.7_Ax\n\""); + add(globals, "\"%07.7_ax \" 8/2 \" %05u \" \"\\n\""); + break; + case 'e': + add(globals, getopt_reent.optarg); + break; + case 'f': + addfile(globals, getopt_reent.optarg); + break; + case 'n': + if ((length = atoi(getopt_reent.optarg)) < 0) + errx(exit_jump, 1, "%s: bad length value", getopt_reent.optarg); + break; + case 'o': + add(globals, "\"%07.7_Ax\n\""); + add(globals, "\"%07.7_ax \" 8/2 \" %06o \" \"\\n\""); + break; + case 's': + if ((skip = strtoll(getopt_reent.optarg, &p, 0)) < 0) + errx(exit_jump, 1, "%s: bad skip value", getopt_reent.optarg); + switch(*p) { + case 'b': + skip *= 512; + break; + case 'k': + skip *= 1024; + break; + case 'm': + skip *= 1048576; + break; + } + break; + case 'v': + vflag = ALL; + break; + case 'x': + add(globals, "\"%07.7_Ax\n\""); + add(globals, "\"%07.7_ax \" 8/2 \" %04x \" \"\\n\""); + break; + case '?': + usage(globals); + } + + if (!fshead) { + add(globals, "\"%07.7_Ax\n\""); + add(globals, "\"%07.7_ax \" 8/2 \"%04x \" \"\\n\""); + } + + *argvp += getopt_reent.optind; +} + +void +usage(rtems_shell_hexdump_globals* globals) +{ + (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", +"usage: hexdump [-bcCdovx] [-e fmt] [-f fmt_file] [-n length]", +" [-s skip] [file ...]", +" hd [-bcdovx] [-e fmt] [-f fmt_file] [-n length]", +" [-s skip] [file ...]"); + exit(1); +} diff --git a/cpukit/libmisc/shell/internal.h b/cpukit/libmisc/shell/internal.h new file mode 100644 index 0000000000..aadd346092 --- /dev/null +++ b/cpukit/libmisc/shell/internal.h @@ -0,0 +1,37 @@ +/* + * Shell Internal Information + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef _RTEMS_SHELL_INTERNAL_H +#define _RTEMS_SHELL_INTERNAL_H + +struct rtems_shell_topic_tt; +typedef struct rtems_shell_topic_tt rtems_shell_topic_t; + +struct rtems_shell_topic_tt { + const char *topic; + rtems_shell_topic_t *next; +}; + + +extern rtems_shell_cmd_t * rtems_shell_first_cmd; +extern rtems_shell_topic_t * rtems_shell_first_topic; + +rtems_shell_topic_t * rtems_shell_lookup_topic(const char *topic); + + +void rtems_shell_register_monitor_commands(void); +void rtems_shell_initialize_command_set(void); + +void rtems_shell_print_heap_info( + const char *c, + Heap_Information *h +); + +#endif diff --git a/cpukit/libmisc/shell/login_check.c b/cpukit/libmisc/shell/login_check.c new file mode 100644 index 0000000000..d744ee5954 --- /dev/null +++ b/cpukit/libmisc/shell/login_check.c @@ -0,0 +1,62 @@ +/** + * @file + * + * @brief Shell login check function. + */ + +/* + * Copyright (c) 2009 embedded brains GmbH and others. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * Based on work from Chris Johns and Fernando Ruiz. + * + * Derived from file "cpukit/libmisc/shell/shell.c". + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <unistd.h> +#include <pwd.h> + +#include <rtems/shell.h> +#include <rtems/userenv.h> + +bool rtems_shell_login_check( + const char *user, + const char *passphrase +) +{ + struct passwd *pw = getpwnam( user); + + /* Valid user? */ + if (pw != NULL && strcmp( pw->pw_passwd, "!") != 0) { + setuid( pw->pw_uid); + setgid( pw->pw_gid); + rtems_current_user_env->euid = 0; + rtems_current_user_env->egid = 0; + chown( rtems_current_shell_env->devname, pw->pw_uid, 0); + rtems_current_user_env->euid = pw->pw_uid; + rtems_current_user_env->egid = pw->pw_gid; + if (strcmp( pw->pw_passwd, "*") == 0) { + /* TODO: /etc/shadow */ + return true; + } else { + /* TODO: crypt() */ + return true; + } + } + + return false; +} diff --git a/cpukit/libmisc/shell/login_prompt.c b/cpukit/libmisc/shell/login_prompt.c new file mode 100644 index 0000000000..7e93078805 --- /dev/null +++ b/cpukit/libmisc/shell/login_prompt.c @@ -0,0 +1,209 @@ +/** + * @file + * + * @brief Shell login prompt functions. + */ + +/* + * Authorship + * ---------- + * Parts of this software was created by + * Till Straumann <strauman@slac.stanford.edu>, 2003-2007 + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * Parts of this software was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + * + * Copyright (c) 2009 embedded brains GmbH and others. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * Based on work from Chris Johns, Fernando Ruiz and Till Straumann. + * + * Derived from files "cpukit/libmisc/shell/shell.c" and + * "cpukit/telnetd/check_passwd.c". + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <termios.h> +#include <unistd.h> +#include <ctype.h> +#include <errno.h> + +#include <rtems/shell.h> + +static int rtems_shell_discard( int c, FILE *stream) +{ + return c; +} + +static bool rtems_shell_get_text( + FILE *in, + FILE *out, + char *line, + size_t size +) +{ + int fd_in = fileno( in); + int (*put)( int, FILE *) = + out != NULL && isatty( fd_in) + ? fputc + : rtems_shell_discard; + size_t i = 0; + + if (size < 1) { + return false; + } + + tcdrain( fd_in); + if (out != NULL){ + tcdrain( fileno(out) ); + } + + while (true) { + int c = fgetc(in); + + switch (c) { + case EOF: + clearerr( in ); + return false; + case '\n': + case '\r': + put('\n', out); + line [i] = '\0'; + return true; + case 127: + case '\b': + if (i > 0) { + put('\b', out); + put(' ', out); + put('\b', out); + --i; + } else { + put('\a', out); + } + break; + default: + if (!iscntrl( c)) { + if (i < size - 1) { + line [i] = (char) c; + ++i; + put( c, out); + } else { + put('\a', out); + } + } else { + put('\a', out); + } + break; + } + } + return true; +} + +bool rtems_shell_login_prompt( + FILE *in, + FILE *out, + const char *device, + rtems_shell_login_check_t check +) +{ + int fd_in = fileno(in); + struct termios termios_previous; + bool restore_termios = false; + int i = 0; + bool result = false; + + if (tcgetattr( fd_in, &termios_previous) == 0) { + struct termios termios_new = termios_previous; + + /* + * Stay in canonical mode so we can tell EOF and dropped connections. + * But read one character at a time and do not echo it. + */ + termios_new.c_lflag &= (unsigned char) ~ECHO; + termios_new.c_cc [VTIME] = 0; + termios_new.c_cc [VMIN] = 1; + + restore_termios = tcsetattr( fd_in, TCSANOW, &termios_new) == 0; + } + + for (i = 0; i < 3; ++i) { + char user [32]; + char passphrase [128]; + + fprintf( out, "%s login: ", device ); + fflush( out ); + result = rtems_shell_get_text( in, out, user, sizeof(user) ); + if ( !result ) + break; + + fflush( in); + fprintf( out, "Password: "); + fflush( out); + result = rtems_shell_get_text( in, NULL, passphrase, sizeof(passphrase) ); + if ( !result ) + break; + fputc( '\n', out); + + result = check( user, passphrase ); + if (result) + break; + + fprintf( out, "Login incorrect\n\n"); + sleep( 2); + } + + if (restore_termios) { + /* What to do if restoring the flags fails? */ + tcsetattr( fd_in, TCSANOW, &termios_previous); + } + + return result; +} diff --git a/cpukit/libmisc/shell/main_alias.c b/cpukit/libmisc/shell/main_alias.c new file mode 100644 index 0000000000..9f1b094eaa --- /dev/null +++ b/cpukit/libmisc/shell/main_alias.c @@ -0,0 +1,45 @@ +/* + * ALIAS Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_rtems_main_alias(int argc, char **argv) +{ + if (argc<3) { + fprintf(stderr,"too few arguments\n"); + return 1; + } + + if (!rtems_shell_alias_cmd(argv[1],argv[2])) { + fprintf(stderr,"unable to make an alias(%s,%s)\n",argv[1],argv[2]); + } + return 0; +} + +rtems_shell_cmd_t rtems_shell_ALIAS_Command = { + "alias", /* name */ + "alias old new", /* usage */ + "misc", /* topic */ + rtems_shell_rtems_main_alias, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_blksync.c b/cpukit/libmisc/shell/main_blksync.c new file mode 100644 index 0000000000..bf8369570e --- /dev/null +++ b/cpukit/libmisc/shell/main_blksync.c @@ -0,0 +1,77 @@ +/* + * RM Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/bdbuf.h> +#include <rtems/blkdev.h> +#include "internal.h" + +int rtems_shell_main_blksync( + int argc, + char *argv[] +) +{ + const char* driver = NULL; + int arg; + int fd; + + for (arg = 1; arg < argc; arg++) { + if (argv[arg][0] == '-') { + fprintf( stderr, "%s: invalid option: %s\n", argv[0], argv[arg]); + return 1; + } else { + if (!driver) + driver = argv[arg]; + else { + fprintf( stderr, "%s: only one driver name allowed: %s\n", + argv[0], argv[arg]); + return 1; + } + } + } + + fd = open (driver, O_WRONLY, 0); + if (fd < 0) { + fprintf( stderr, "%s: driver open failed: %s\n", argv[0], strerror (errno)); + return 1; + } + + if (ioctl (fd, RTEMS_BLKIO_SYNCDEV) < 0) { + fprintf( stderr, "%s: driver sync failed: %s\n", argv[0], strerror (errno)); + return 1; + } + + close (fd); + return 0; +} + +rtems_shell_cmd_t rtems_shell_BLKSYNC_Command = { + "blksync", /* name */ + "blksync driver # sync the block driver", /* usage */ + "files", /* topic */ + rtems_shell_main_blksync, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_cat.c b/cpukit/libmisc/shell/main_cat.c new file mode 100644 index 0000000000..6efd7a09c8 --- /dev/null +++ b/cpukit/libmisc/shell/main_cat.c @@ -0,0 +1,60 @@ +/* + * CAT Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <termios.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <dirent.h> +#include <time.h> +#include <fcntl.h> +#include <unistd.h> +#include <pwd.h> +#include <grp.h> +#include <errno.h> +#include <sys/types.h> +#include <stddef.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_cat(int argc, char *argv[]) +{ + int n; + int sc; + + for ( n=1; n < argc ; n++) { + sc = rtems_shell_cat_file(stdout, argv[n]); + if ( sc == -1 ) { + fprintf(stderr, "%s: %s: %s\n", argv[0], argv[n], strerror(errno)); + return -1; + } + } + return 0; +} + +rtems_shell_cmd_t rtems_shell_CAT_Command = { + "cat", /* name */ + "cat n1 [n2 [n3...]] # show the ascii contents", /* usage */ + "files", /* topic */ + rtems_shell_main_cat , /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_cd.c b/cpukit/libmisc/shell/main_cd.c new file mode 100644 index 0000000000..e3ed35fa63 --- /dev/null +++ b/cpukit/libmisc/shell/main_cd.c @@ -0,0 +1,26 @@ +/* + * CD Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +rtems_shell_alias_t rtems_shell_CD_Alias = { + "chdir", /* command */ + "cd" /* alias */ +}; diff --git a/cpukit/libmisc/shell/main_chdir.c b/cpukit/libmisc/shell/main_chdir.c new file mode 100644 index 0000000000..2273f5c956 --- /dev/null +++ b/cpukit/libmisc/shell/main_chdir.c @@ -0,0 +1,54 @@ +/* + * CHDIR Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_chdir( + int argc, + char *argv[] +) +{ + char *dir; + + dir = "/"; + + if (argc > 1) + dir = argv[1]; + + if (chdir(dir)) { + fprintf(stderr, "chdir to '%s' failed:%s\n", dir,strerror(errno)); + return errno; + } + return 0; +} + +rtems_shell_cmd_t rtems_shell_CHDIR_Command = { + "chdir", /* name */ + "chdir [dir] # change the current directory", /* usage */ + "files", /* topic */ + rtems_shell_main_chdir, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_chmod.c b/cpukit/libmisc/shell/main_chmod.c new file mode 100644 index 0000000000..3c6ae68310 --- /dev/null +++ b/cpukit/libmisc/shell/main_chmod.c @@ -0,0 +1,70 @@ +/* + * CHMOD Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/stringto.h> +#include "internal.h" + +int rtems_shell_main_chmod( + int argc, + char *argv[] +) +{ + int n; + mode_t mode; + unsigned long tmp; + + if (argc < 2) { + fprintf(stderr,"%s: too few arguments\n", argv[0]); + return -1; + } + + /* + * Convert arguments into numbers + */ + if ( rtems_string_to_unsigned_long(argv[1], &tmp, NULL, 0) ) { + printf( "Mode argument (%s) is not a number\n", argv[1] ); + return -1; + } + mode = (mode_t) (tmp & 0777); + + /* + * Now change the files modes + */ + for (n=2 ; n < argc ; n++) + chmod(argv[n++], mode); + + return 0; +} + +rtems_shell_cmd_t rtems_shell_CHMOD_Command = { + "chmod", /* name */ + "chmod 0777 n1 n2... # change filemode", /* usage */ + "files", /* topic */ + rtems_shell_main_chmod, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_chroot.c b/cpukit/libmisc/shell/main_chroot.c new file mode 100644 index 0000000000..ffdfb9ca3b --- /dev/null +++ b/cpukit/libmisc/shell/main_chroot.c @@ -0,0 +1,53 @@ +/* + * CHROOT Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_chroot( + int argc, + char * argv[] +) +{ + char *new_root = "/"; + + if (argc == 2) + new_root = argv[1]; + + if ( chroot(new_root) < 0 ) { + fprintf(stderr,"chroot %s (%s)\n", new_root, strerror(errno)); + return -1; + } + + return 0; +} + +rtems_shell_cmd_t rtems_shell_CHROOT_Command = { + "chroot", /* name */ + "chroot [dir] # change the root directory", /* usage */ + "files", /* topic */ + rtems_shell_main_chroot, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_cp.c b/cpukit/libmisc/shell/main_cp.c new file mode 100644 index 0000000000..5b56235abd --- /dev/null +++ b/cpukit/libmisc/shell/main_cp.c @@ -0,0 +1,555 @@ +/* $NetBSD: cp.c,v 1.39 2005/10/24 12:59:07 kleink Exp $ */ + +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * David Hitz of Auspex Systems Inc. + * + * 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if 0 +#ifndef lint +__COPYRIGHT( +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cp.c 8.5 (Berkeley) 4/29/95"; +#else +__RCSID("$NetBSD: cp.c,v 1.39 2005/10/24 12:59:07 kleink Exp $"); +#endif +#endif /* not lint */ +#endif +/* + * Cp copies source files to target files. + * + * The global PATH_T structure "to" always contains the path to the + * current target file. Since fts(3) does not change directories, + * this path can be either absolute or dot-relative. + * + * The basic algorithm is to initialize "to" and use fts(3) to traverse + * the file hierarchy rooted in the argument list. A trivial case is the + * case of 'cp file1 file2'. The more interesting case is the case of + * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the + * path (relative to the root of the traversal) is appended to dir (stored + * in "to") to form the final target path. + */ + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/shellconfig.h> +#define __need_getopt_newlib +#include <getopt.h> + +#include <sys/param.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <limits.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "extern-cp.h" + +#define S_ISTXT 0 + +#define STRIP_TRAILING_SLASH(p) { \ + while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \ + *--(p).p_end = 0; \ +} + +enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; + +static int Rflag, rflag; + +static int main_cp(rtems_shell_cp_globals* cp_globals, int, char *[]); +static int copy(rtems_shell_cp_globals* cp_globals, char *[], enum op, int); +static int mastercmp(const FTSENT **, const FTSENT **); + +void +rtems_shell_cp_exit (rtems_shell_cp_globals* cp_global, int code __attribute__((unused))) +{ + longjmp (cp_global->exit_jmp, 1); +} + +int +rtems_shell_main_cp(int argc, char *argv[]) +{ + rtems_shell_cp_globals cp_globals; + memset (&cp_globals, 0, sizeof (cp_globals)); + Rflag = rflag = 0; + if (setjmp (cp_globals.exit_jmp) == 0) + return main_cp (&cp_globals, argc, argv); + return 1; +} + +int +main_cp(rtems_shell_cp_globals* cp_globals, int argc, char *argv[]) +{ + struct stat to_stat, tmp_stat; + enum op type; + int Hflag, Lflag, Pflag, ch, fts_options, r, have_trailing_slash; + char *target; + struct getopt_data getopt_reent; + + Hflag = Lflag = Pflag = 0; + memset(&getopt_reent, 0, sizeof(getopt_data)); + + while ((ch = getopt_r(argc, argv, "HLPRafilnprv", &getopt_reent)) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = Pflag = 0; + break; + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'a': + Pflag = 1; + pflag = 1; + Rflag = 1; + Hflag = Lflag = 0; + break; + case 'f': + fflag = 1; + iflag = nflag = 0; + break; + case 'i': + iflag = 1; + fflag = nflag = 0; + break; + case 'l': + lflag = 1; + break; + case 'n': + nflag = 1; + fflag = iflag = 0; + break; + case 'p': + pflag = 1; + break; + case 'r': + rflag = Lflag = 1; + Hflag = Pflag = 0; + break; + case 'v': + vflag = 1; + break; + default: + usage(cp_globals); + break; + } + argc -= getopt_reent.optind; + argv += getopt_reent.optind; + + if (argc < 2) + usage(cp_globals); + + fts_options = FTS_NOCHDIR | FTS_PHYSICAL; + if (Rflag && rflag) + errx(exit_jump, 1, "the -R and -r options may not be specified together"); + if (rflag) + Rflag = 1; + if (Rflag) { + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL | FTS_COMFOLLOW; + } +#if 0 + (void)signal(SIGINFO, siginfo); +#endif + + /* Save the target base in "to". */ + target = argv[--argc]; + if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path)) + errx(exit_jump, 1, "%s: name too long", target); + to.p_end = to.p_path + strlen(to.p_path); + if (to.p_path == to.p_end) { + *to.p_end++ = '.'; + *to.p_end = 0; + } + have_trailing_slash = (to.p_end[-1] == '/'); + if (have_trailing_slash) + STRIP_TRAILING_SLASH(to); + to.target_end = to.p_end; + + /* Set end of argument list for fts(3). */ + argv[argc] = NULL; + + /* + * Cp has two distinct cases: + * + * cp [-R] source target + * cp [-R] source1 ... sourceN directory + * + * In both cases, source can be either a file or a directory. + * + * In (1), the target becomes a copy of the source. That is, if the + * source is a file, the target will be a file, and likewise for + * directories. + * + * In (2), the real target is not directory, but "directory/source". + */ + r = stat(to.p_path, &to_stat); + if (r == -1 && errno != ENOENT) + err(exit_jump, 1, "%s", to.p_path); + if (r == -1 || !S_ISDIR(to_stat.st_mode)) { + /* + * Case (1). Target is not a directory. + */ + if (argc > 1) + errx(exit_jump, 1, "%s is not a directory", to.p_path); + + /* + * Need to detect the case: + * cp -R dir foo + * Where dir is a directory and foo does not exist, where + * we want pathname concatenations turned on but not for + * the initial mkdir(). + */ + if (r == -1) { + if (Rflag && (Lflag || Hflag)) + stat(*argv, &tmp_stat); + else + lstat(*argv, &tmp_stat); + + if (S_ISDIR(tmp_stat.st_mode) && Rflag) + type = DIR_TO_DNE; + else + type = FILE_TO_FILE; + } else + type = FILE_TO_FILE; + + if (have_trailing_slash && type == FILE_TO_FILE) { + if (r == -1) + errx(exit_jump, 1, "directory %s does not exist", + to.p_path); + else + errx(exit_jump, 1, "%s is not a directory", to.p_path); + } + } else + /* + * Case (2). Target is a directory. + */ + type = FILE_TO_DIR; + + return copy(cp_globals, argv, type, fts_options); +} + +int +copy(rtems_shell_cp_globals* cp_globals, + char *argv[], enum op type, int fts_options) +{ + struct stat to_stat; + FTS *ftsp; + FTSENT *curr; + int base = 0, dne, badcp, rval; + size_t nlen; + char *p, *target_mid; + mode_t mask, mode; + + /* + * Keep an inverted copy of the umask, for use in correcting + * permissions on created directories when not using -p. + */ + mask = ~umask(0777); + umask(~mask); + + if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL) + err(exit_jump, 1, "fts_open"); + for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) { + switch (curr->fts_info) { + case FTS_NS: + case FTS_DNR: + case FTS_ERR: + warnx("%s: %s", + curr->fts_path, strerror(curr->fts_errno)); + badcp = rval = 1; + continue; + case FTS_DC: /* Warn, continue. */ + warnx("%s: directory causes a cycle", curr->fts_path); + badcp = rval = 1; + continue; + default: + ; + } + + /* + * If we are in case (2) or (3) above, we need to append the + * source name to the target name. + */ + if (type != FILE_TO_FILE) { + /* + * Need to remember the roots of traversals to create + * correct pathnames. If there's a directory being + * copied to a non-existent directory, e.g. + * cp -R a/dir noexist + * the resulting path name should be noexist/foo, not + * noexist/dir/foo (where foo is a file in dir), which + * is the case where the target exists. + * + * Also, check for "..". This is for correct path + * concatenation for paths ending in "..", e.g. + * cp -R .. /tmp + * Paths ending in ".." are changed to ".". This is + * tricky, but seems the easiest way to fix the problem. + * + * XXX + * Since the first level MUST be FTS_ROOTLEVEL, base + * is always initialized. + */ + if (curr->fts_level == FTS_ROOTLEVEL) { + if (type != DIR_TO_DNE) { + p = strrchr(curr->fts_path, '/'); + base = (p == NULL) ? 0 : + (int)(p - curr->fts_path + 1); + + if (!strcmp(&curr->fts_path[base], + "..")) + base += 1; + } else + base = curr->fts_pathlen; + } + + p = &curr->fts_path[base]; + nlen = curr->fts_pathlen - base; + target_mid = to.target_end; + if (*p != '/' && target_mid[-1] != '/') + *target_mid++ = '/'; + *target_mid = 0; + if (target_mid - to.p_path + nlen >= PATH_MAX) { + warnx("%s%s: name too long (not copied)", + to.p_path, p); + badcp = rval = 1; + continue; + } + (void)strncat(target_mid, p, nlen); + to.p_end = target_mid + nlen; + *to.p_end = 0; + STRIP_TRAILING_SLASH(to); + } + + if (curr->fts_info == FTS_DP) { + /* + * We are nearly finished with this directory. If we + * didn't actually copy it, or otherwise don't need to + * change its attributes, then we are done. + */ + if (!curr->fts_number) + continue; + /* + * If -p is in effect, set all the attributes. + * Otherwise, set the correct permissions, limited + * by the umask. Optimise by avoiding a chmod() + * if possible (which is usually the case if we + * made the directory). Note that mkdir() does not + * honour setuid, setgid and sticky bits, but we + * normally want to preserve them on directories. + */ + if (pflag) { + if (setfile(cp_globals, curr->fts_statp, -1)) + rval = 1; + if (preserve_dir_acls(curr->fts_statp, + curr->fts_accpath, to.p_path) != 0) + rval = 1; + } else { + mode = curr->fts_statp->st_mode; + if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) || + ((mode | S_IRWXU) & mask) != (mode & mask)) + if (chmod(to.p_path, mode & mask) != 0){ + warn("chmod: %s", to.p_path); + rval = 1; + } + } + continue; + } + + /* Not an error but need to remember it happened */ + if (stat(to.p_path, &to_stat) == -1) + dne = 1; + else { + if (to_stat.st_dev == curr->fts_statp->st_dev && + to_stat.st_ino == curr->fts_statp->st_ino) { + warnx("%s and %s are identical (not copied).", + to.p_path, curr->fts_path); + badcp = rval = 1; + if (S_ISDIR(curr->fts_statp->st_mode)) + (void)fts_set(ftsp, curr, FTS_SKIP); + continue; + } + if (!S_ISDIR(curr->fts_statp->st_mode) && + S_ISDIR(to_stat.st_mode)) { + warnx("cannot overwrite directory %s with " + "non-directory %s", + to.p_path, curr->fts_path); + badcp = rval = 1; + continue; + } + dne = 0; + } + + switch (curr->fts_statp->st_mode & S_IFMT) { + case S_IFLNK: + /* Catch special case of a non-dangling symlink */ + if ((fts_options & FTS_LOGICAL) || + ((fts_options & FTS_COMFOLLOW) && + curr->fts_level == 0)) { + if (copy_file(cp_globals, curr, dne)) + badcp = rval = 1; + } else { + if (copy_link(cp_globals, curr, !dne)) + badcp = rval = 1; + } + break; + case S_IFDIR: + if (!Rflag) { + warnx("%s is a directory (not copied).", + curr->fts_path); + (void)fts_set(ftsp, curr, FTS_SKIP); + badcp = rval = 1; + break; + } + /* + * If the directory doesn't exist, create the new + * one with the from file mode plus owner RWX bits, + * modified by the umask. Trade-off between being + * able to write the directory (if from directory is + * 555) and not causing a permissions race. If the + * umask blocks owner writes, we fail.. + */ + if (dne) { + if (mkdir(to.p_path, + curr->fts_statp->st_mode | S_IRWXU) < 0) + err(exit_jump, 1, "%s", to.p_path); + } else if (!S_ISDIR(to_stat.st_mode)) { + errno = ENOTDIR; + err(exit_jump, 1, "%s", to.p_path); + } + /* + * Arrange to correct directory attributes later + * (in the post-order phase) if this is a new + * directory, or if the -p flag is in effect. + */ + curr->fts_number = pflag || dne; + break; + case S_IFBLK: + case S_IFCHR: + if (Rflag) { + if (copy_special(cp_globals, curr->fts_statp, !dne)) + badcp = rval = 1; + } else { + if (copy_file(cp_globals, curr, dne)) + badcp = rval = 1; + } + break; + case S_IFSOCK: + warnx("%s is a socket (not copied).", + curr->fts_path); + case S_IFIFO: + if (Rflag) { + if (copy_fifo(cp_globals, curr->fts_statp, !dne)) + badcp = rval = 1; + } else { + if (copy_file(cp_globals, curr, dne)) + badcp = rval = 1; + } + break; + default: + if (copy_file(cp_globals, curr, dne)) + badcp = rval = 1; + break; + } + if (vflag && !badcp) + (void)printf("%s -> %s\n", curr->fts_path, to.p_path); + } + if (errno) + err(exit_jump, 1, "fts_read"); + fts_close(ftsp); + return (rval); +} + +/* + * mastercmp -- + * The comparison function for the copy order. The order is to copy + * non-directory files before directory files. The reason for this + * is because files tend to be in the same cylinder group as their + * parent directory, whereas directories tend not to be. Copying the + * files first reduces seeking. + */ +int +mastercmp(const FTSENT **a, const FTSENT **b) +{ + int a_info, b_info; + + a_info = (*a)->fts_info; + if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR) + return (0); + b_info = (*b)->fts_info; + if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR) + return (0); + if (a_info == FTS_D) + return (-1); + if (b_info == FTS_D) + return (1); + return (0); +} + +rtems_shell_cmd_t rtems_shell_CP_Command = { + "cp", /* name */ + "cp [-R [-H | -L | -P]] [-f | -i] [-pv] src target", /* usage */ + "files", /* topic */ + rtems_shell_main_cp, /* command */ + NULL, /* alias */ + NULL /* next */ +}; + diff --git a/cpukit/libmisc/shell/main_cpuuse.c b/cpukit/libmisc/shell/main_cpuuse.c new file mode 100644 index 0000000000..b2329934bd --- /dev/null +++ b/cpukit/libmisc/shell/main_cpuuse.c @@ -0,0 +1,61 @@ +/* + * CPUUSE Command Implementation + * + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> + +#include <rtems.h> +#include <rtems/cpuuse.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_cpuuse( + int argc, + char *argv[] +) +{ + /* + * When invoked with no arguments, print the report. + */ + if ( argc == 1 ) { + rtems_cpu_usage_report_with_plugin(stdout, (rtems_printk_plugin_t)fprintf); + return 0; + } + + /* + * When invoked with the single argument -r, reset the statistics. + */ + if ( argc == 2 && !strcmp( argv[1], "-r" ) ) { + printf( "Resetting CPU Usage information\n" ); + rtems_cpu_usage_reset(); + return 0; + } + + /* + * OK. The user did something wrong. + */ + fprintf( stderr, "%s: [-r]\n", argv[0] ); + return -1; +} + +rtems_shell_cmd_t rtems_shell_CPUUSE_Command = { + "cpuuse", /* name */ + "[-r] print or reset per thread cpu usage", /* usage */ + "rtems", /* topic */ + rtems_shell_main_cpuuse, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_date.c b/cpukit/libmisc/shell/main_date.c new file mode 100644 index 0000000000..da6faa0e53 --- /dev/null +++ b/cpukit/libmisc/shell/main_date.c @@ -0,0 +1,80 @@ +/* + * DATE Shell Command Implmentation + * + * OAuthor: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * Significantly rewritten by Joel Sherrill <joel.sherrill@oarcorp.com>. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_date( + int argc, + char *argv[] +) +{ + /* + * Print the current date and time in default format. + */ + if ( argc == 1 ) { + time_t t; + + time(&t); + printf("%s", ctime(&t)); + return 0; + } + + /* + * Set the current date and time + */ + if ( argc == 3 ) { + char buf[128]; + struct tm TOD; + struct timespec timesp; + char *result; + + snprintf( buf, sizeof(buf), "%s %s", argv[1], argv[2] ); + result = strptime( + buf, + "%Y-%m-%d %T", + &TOD + ); + if ( result && !*result ) { + timesp.tv_sec = mktime( &TOD ); + timesp.tv_nsec = 0; + clock_settime( CLOCK_REALTIME, ×p ); + return 0; + } + } + + fprintf( stderr, "%s: Usage: [YYYY-MM-DD HH:MM:SS]\n", argv[0] ); + return -1; +} + +rtems_shell_cmd_t rtems_shell_DATE_Command = { + "date", /* name */ + "date [YYYY-MM-DD HH:MM:SS]", /* usage */ + "misc", /* topic */ + rtems_shell_main_date, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_dd.c b/cpukit/libmisc/shell/main_dd.c new file mode 100644 index 0000000000..930d382a97 --- /dev/null +++ b/cpukit/libmisc/shell/main_dd.c @@ -0,0 +1,565 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if 0 +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)dd.c 8.5 (Berkeley) 4/2/94"; +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/bin/dd/dd.c,v 1.43 2004/08/15 19:10:05 rwatson Exp $"); +#endif + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/shellconfig.h> + +#include <sys/param.h> +#include <sys/stat.h> +#if RTEMS_REMOVED +#include <sys/conf.h> +#include <sys/disklabel.h> +#endif +#include <sys/filio.h> +#include <sys/time.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "dd.h" +#include "extern-dd.h" + +#define DD_DEFFILEMODE 0 + +static void dd_close(rtems_shell_dd_globals* globals); +static void dd_in(rtems_shell_dd_globals* globals); +static void getfdtype(rtems_shell_dd_globals* globals, IO *); +static void setup(rtems_shell_dd_globals* globals); + +#if RTEMS_REMOVED +IO in, out; /* input/output state */ +STAT st; /* statistics */ +void (*cfunc)(void); /* conversion function */ +uintmax_t cpy_cnt; /* # of blocks to copy */ +static off_t pending = 0; /* pending seek if sparse */ +u_int ddflags = 0; /* conversion options */ +size_t cbsz; /* conversion block size */ +uintmax_t files_cnt = 1; /* # of files to copy */ +const u_char *ctab; /* conversion table */ +char fill_char; /* Character to fill with if defined */ +#endif + +static off_t pending = 0; /* pending seek if sparse */ + +void +rtems_shell_dd_exit (rtems_shell_dd_globals* globals, int code) +{ + globals->exit_code = code; + longjmp (globals->exit_jmp, 1); +} + +static int main_dd(rtems_shell_dd_globals* globals, int argc, char *argv[]); + +int +rtems_shell_main_dd(int argc, char *argv[]) +{ + rtems_shell_dd_globals dd_globals; + rtems_shell_dd_globals* globals = &dd_globals; + memset (globals, 0, sizeof (dd_globals)); + pending = 0; + ddflags = 0; + files_cnt = 1; + dd_globals.exit_code = 1; + if (setjmp (dd_globals.exit_jmp) == 0) + dd_globals.exit_code = main_dd (globals, argc, argv); + if (in.fd) + close(in.fd); + if (out.fd) + close(out.fd); + if (in.name) + free((void*)in.name); + if (out.name) + free((void*)out.name); + if (in.db) + free(in.db); + if (out.db && (in.db != out.db)) + free(out.db); + return dd_globals.exit_code; +} + +int +main_dd(rtems_shell_dd_globals* globals, int argc __unused, char *argv[]) +{ + (void)setlocale(LC_CTYPE, ""); + jcl(globals, argv); + setup(globals); + +#if RTEMS_REMOVED + (void)signal(SIGINFO, summaryx); + (void)signal(SIGINT, terminate); + + atexit(summary); +#endif + + while (files_cnt--) + dd_in(globals); + + dd_close(globals); + exit(0); + /* NOTREACHED */ + return 0; +} + +static int +parity(u_char c) +{ + int i; + + i = c ^ (c >> 1) ^ (c >> 2) ^ (c >> 3) ^ + (c >> 4) ^ (c >> 5) ^ (c >> 6) ^ (c >> 7); + return (i & 1); +} + +static void +setup(rtems_shell_dd_globals* globals) +{ + u_int cnt; + struct timeval tv; + + if (in.name == NULL) { + in.name = "stdin"; + in.fd = STDIN_FILENO; + } else { + in.fd = open(in.name, O_RDONLY, 0); + if (in.fd == -1) + err(exit_jump, 1, "%s", in.name); + } + + getfdtype(globals, &in); + + if (files_cnt > 1 && !(in.flags & ISTAPE)) + errx(exit_jump, 1, "files is not supported for non-tape devices"); + + if (out.name == NULL) { + /* No way to check for read access here. */ + out.fd = STDOUT_FILENO; + out.name = "stdout"; + } else { +#define OFLAGS \ + (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC)) + out.fd = open(out.name, O_RDWR | OFLAGS, DD_DEFFILEMODE); + /* + * May not have read access, so try again with write only. + * Without read we may have a problem if output also does + * not support seeks. + */ + if (out.fd == -1) { + out.fd = open(out.name, O_WRONLY | OFLAGS, DD_DEFFILEMODE); + out.flags |= NOREAD; + } + if (out.fd == -1) + err(exit_jump, 1, "%s", out.name); + } + + getfdtype(globals, &out); + + /* + * Allocate space for the input and output buffers. If not doing + * record oriented I/O, only need a single buffer. + */ + if (!(ddflags & (C_BLOCK | C_UNBLOCK))) { + if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL) + err(exit_jump, 1, "input buffer"); + out.db = in.db; + } else if ((in.db = malloc(MAX(in.dbsz, cbsz) + cbsz)) == NULL || + (out.db = malloc(out.dbsz + cbsz)) == NULL) + err(exit_jump, 1, "output buffer"); + in.dbp = in.db; + out.dbp = out.db; + + /* Position the input/output streams. */ + if (in.offset) + pos_in(globals); + if (out.offset) + pos_out(globals); + + /* + * Truncate the output file. If it fails on a type of output file + * that it should _not_ fail on, error out. + */ + if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK) && + out.flags & ISTRUNC) + if (ftruncate(out.fd, out.offset * out.dbsz) == -1) + err(exit_jump, 1, "truncating %s", out.name); + + if (ddflags & (C_LCASE | C_UCASE | C_ASCII | C_EBCDIC | C_PARITY)) { + if (ctab != NULL) { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = ctab[cnt]; + } else { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = cnt; + } + if ((ddflags & C_PARITY) && !(ddflags & C_ASCII)) { + /* + * If the input is not EBCDIC, and we do parity + * processing, strip input parity. + */ + for (cnt = 200; cnt <= 0377; ++cnt) + casetab[cnt] = casetab[cnt & 0x7f]; + } + if (ddflags & C_LCASE) { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = tolower(casetab[cnt]); + } else if (ddflags & C_UCASE) { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = toupper(casetab[cnt]); + } + if ((ddflags & C_PARITY)) { + /* + * This should strictly speaking be a no-op, but I + * wonder what funny LANG settings could get us. + */ + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = casetab[cnt] & 0x7f; + } + if ((ddflags & C_PARSET)) { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = casetab[cnt] | 0x80; + } + if ((ddflags & C_PAREVEN)) { + for (cnt = 0; cnt <= 0377; ++cnt) + if (parity(casetab[cnt])) + casetab[cnt] = casetab[cnt] | 0x80; + } + if ((ddflags & C_PARODD)) { + for (cnt = 0; cnt <= 0377; ++cnt) + if (!parity(casetab[cnt])) + casetab[cnt] = casetab[cnt] | 0x80; + } + + ctab = casetab; + } + + (void)gettimeofday(&tv, (struct timezone *)NULL); + st.start = tv.tv_sec + tv.tv_usec * 1e-6; +} + +static void +getfdtype(rtems_shell_dd_globals* globals, IO *io) +{ + struct stat sb; +#if RTEMS_REMOVED + int type; +#endif + + if (fstat(io->fd, &sb) == -1) + err(exit_jump, 1, "%s", io->name); + if (S_ISREG(sb.st_mode)) + io->flags |= ISTRUNC; +#if RTEMS_REMOVED + if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) { + if (ioctl(io->fd, FIODTYPE, &type) == -1) { + err(exit_jump, 1, "%s", io->name); + } else { + if (type & D_TAPE) + io->flags |= ISTAPE; + else if (type & (D_DISK | D_MEM)) + io->flags |= ISSEEK; + if (S_ISCHR(sb.st_mode) && (type & D_TAPE) == 0) + io->flags |= ISCHR; + } + return; + } +#else + io->flags |= ISSEEK; +#endif + errno = 0; + if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) + io->flags |= ISPIPE; + else + io->flags |= ISSEEK; +} + +static void +dd_in(rtems_shell_dd_globals* globals) +{ + ssize_t n; + + for (;;) { + switch (cpy_cnt) { + case -1: /* count=0 was specified */ + return; + case 0: + break; + default: + if (st.in_full + st.in_part >= (uintmax_t)cpy_cnt) + return; + break; + } + + /* + * Zero the buffer first if sync; if doing block operations, + * use spaces. + */ + if (ddflags & C_SYNC) { + if (ddflags & C_FILL) + memset(in.dbp, fill_char, in.dbsz); + else if (ddflags & (C_BLOCK | C_UNBLOCK)) + memset(in.dbp, ' ', in.dbsz); + else + memset(in.dbp, 0, in.dbsz); + } + + n = read(in.fd, in.dbp, in.dbsz); + if (n == 0) { + in.dbrcnt = 0; + return; + } + + /* Read error. */ + if (n == -1) { + /* + * If noerror not specified, die. POSIX requires that + * the warning message be followed by an I/O display. + */ + if (!(ddflags & C_NOERROR)) + err(exit_jump, 1, "%s", in.name); + warn("%s", in.name); + summary(globals); + + /* + * If it's a seekable file descriptor, seek past the + * error. If your OS doesn't do the right thing for + * raw disks this section should be modified to re-read + * in sector size chunks. + */ + if (in.flags & ISSEEK && + lseek(in.fd, (off_t)in.dbsz, SEEK_CUR)) + warn("%s", in.name); + + /* If sync not specified, omit block and continue. */ + if (!(ddflags & C_SYNC)) + continue; + + /* Read errors count as full blocks. */ + in.dbcnt += in.dbrcnt = in.dbsz; + ++st.in_full; + + /* Handle full input blocks. */ + } else if ((size_t)n == in.dbsz) { + in.dbcnt += in.dbrcnt = n; + ++st.in_full; + + /* Handle partial input blocks. */ + } else { + /* If sync, use the entire block. */ + if (ddflags & C_SYNC) + in.dbcnt += in.dbrcnt = in.dbsz; + else + in.dbcnt += in.dbrcnt = n; + ++st.in_part; + } + + /* + * POSIX states that if bs is set and no other conversions + * than noerror, notrunc or sync are specified, the block + * is output without buffering as it is read. + */ + if (ddflags & C_BS) { + out.dbcnt = in.dbcnt; + dd_out(globals, 1); + in.dbcnt = 0; + continue; + } + + if (ddflags & C_SWAB) { + if ((n = in.dbrcnt) & 1) { + ++st.swab; + --n; + } + swab(in.dbp, in.dbp, (size_t)n); + } + + in.dbp += in.dbrcnt; + (*cfunc)(globals); + } +} + +/* + * Clean up any remaining I/O and flush output. If necessary, the output file + * is truncated. + */ +static void +dd_close(rtems_shell_dd_globals* globals) +{ + if (cfunc == def) + def_close(globals); + else if (cfunc == block) + block_close(globals); + else if (cfunc == unblock) + unblock_close(globals); + if (ddflags & C_OSYNC && out.dbcnt && out.dbcnt < out.dbsz) { + if (ddflags & C_FILL) + memset(out.dbp, fill_char, out.dbsz - out.dbcnt); + else if (ddflags & (C_BLOCK | C_UNBLOCK)) + memset(out.dbp, ' ', out.dbsz - out.dbcnt); + else + memset(out.dbp, 0, out.dbsz - out.dbcnt); + out.dbcnt = out.dbsz; + } + if (out.dbcnt || pending) + dd_out(globals, 1); +} + +void +dd_out(rtems_shell_dd_globals* globals, int force) +{ + u_char *outp; + size_t cnt, i, n; + ssize_t nw; + static int warned; + int sparse; + + /* + * Write one or more blocks out. The common case is writing a full + * output block in a single write; increment the full block stats. + * Otherwise, we're into partial block writes. If a partial write, + * and it's a character device, just warn. If a tape device, quit. + * + * The partial writes represent two cases. 1: Where the input block + * was less than expected so the output block was less than expected. + * 2: Where the input block was the right size but we were forced to + * write the block in multiple chunks. The original versions of dd(1) + * never wrote a block in more than a single write, so the latter case + * never happened. + * + * One special case is if we're forced to do the write -- in that case + * we play games with the buffer size, and it's usually a partial write. + */ + outp = out.db; + for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) { + for (cnt = n;; cnt -= nw) { + sparse = 0; + if (ddflags & C_SPARSE) { + sparse = 1; /* Is buffer sparse? */ + for (i = 0; i < cnt; i++) + if (outp[i] != 0) { + sparse = 0; + break; + } + } + if (sparse && !force) { + pending += cnt; + nw = cnt; + } else { + if (pending != 0) { + if (force) + pending--; + if (lseek(out.fd, pending, SEEK_CUR) == + -1) + err(exit_jump, 2, "%s: seek error creating sparse file", + out.name); + if (force) + write(out.fd, outp, 1); + pending = 0; + } + if (cnt) + nw = write(out.fd, outp, cnt); + else + return; + } + + if (nw <= 0) { + if (nw == 0) + errx(exit_jump, 1, "%s: end of device", out.name); + if (errno != EINTR) + err(exit_jump, 1, "%s", out.name); + nw = 0; + } + outp += nw; + st.bytes += nw; + if ((size_t)nw == n) { + if (n != out.dbsz) + ++st.out_part; + else + ++st.out_full; + break; + } + ++st.out_part; + if ((size_t)nw == cnt) + break; + if (out.flags & ISTAPE) + errx(exit_jump, 1, "%s: short write on tape device", + out.name); + if (out.flags & ISCHR && !warned) { + warned = 1; + warnx("%s: short write on character device", + out.name); + } + } + if ((out.dbcnt -= n) < out.dbsz) + break; + } + + /* Reassemble the output block. */ + if (out.dbcnt) + (void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt); + out.dbp = out.db + out.dbcnt; +} + +rtems_shell_cmd_t rtems_shell_DD_Command = { + "dd", /* name */ + "dd [OPERAND]...", /* usage */ + "files", /* topic */ + rtems_shell_main_dd, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_debugrfs.c b/cpukit/libmisc/shell/main_debugrfs.c new file mode 100644 index 0000000000..5d4052d43a --- /dev/null +++ b/cpukit/libmisc/shell/main_debugrfs.c @@ -0,0 +1,35 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/stringto.h> +#include <rtems/shellconfig.h> +#include <rtems/rtems-rfs-shell.h> +#include <rtems/fsmount.h> +#include "internal.h" + +#define OPTIONS "[-h]" + +rtems_shell_cmd_t rtems_shell_DEBUGRFS_Command = { + "debugrfs", /* name */ + "debugrfs " OPTIONS, /* usage */ + "files", /* topic */ + rtems_shell_debugrfs, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_dir.c b/cpukit/libmisc/shell/main_dir.c new file mode 100644 index 0000000000..473ab13f80 --- /dev/null +++ b/cpukit/libmisc/shell/main_dir.c @@ -0,0 +1,26 @@ +/* + * DIR Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +rtems_shell_alias_t rtems_shell_DIR_Alias = { + "ls", /* command */ + "dir" /* alias */ +}; diff --git a/cpukit/libmisc/shell/main_echo.c b/cpukit/libmisc/shell/main_echo.c new file mode 100644 index 0000000000..8f868a2f29 --- /dev/null +++ b/cpukit/libmisc/shell/main_echo.c @@ -0,0 +1,142 @@ +/* $NetBSD: echo.c,v 1.12 2005/02/06 04:43:43 perry Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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 University 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 REGENTS 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 REGENTS 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. + * + * @(#)echo.c 8.1 (Berkeley) 5/31/93 + */ + +/* + * Echo command. + * + * echo is steeped in tradition - several of them! + * netbsd has supported 'echo [-n | -e] args' in spite of -e not being + * documented anywhere. + * Posix requires that -n be supported, output from strings containing + * \ is implementation defined + * The Single Unix Spec requires that \ escapes be treated as if -e + * were set, but that -n not be treated as an option. + * (ksh supports 'echo [-eEn] args', but not -- so that it is actually + * impossible to actually output '-n') + * + * It is suggested that 'printf "%b" "string"' be used to get \ sequences + * expanded. printf is now a builtin of netbsd's sh and csh. + */ + +/* + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + + + +int rtems_shell_main_echo( + int argc, + char *argv[] +) +{ + char **ap; + char *p; + char c; + int count; + int nflag = 0; + int eflag = 0; + + ap = argv; + if (argc) + ap++; + + if ((p = *ap) != NULL) { + if (!strcmp(p, "-n")) { + nflag = 1; + ap++; + } else if (!strcmp(p, "-e")) { + eflag = 1; + ap++; + } + } + + while ((p = *ap++) != NULL) { + while ((c = *p++) != '\0') { + if (c == '\\' && eflag) { + switch (*p++) { + case 'a': c = '\a'; break; /* bell */ + case 'b': c = '\b'; break; + case 'c': return 0; /* exit */ + case 'e': c = 033; break; /* escape */ + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case '\\': break; /* c = '\\' */ + case '0': + c = 0; + count = 3; + while (--count >= 0 && (unsigned)(*p - '0') < 8) + c = (c << 3) + (*p++ - '0'); + break; + default: + /* Output the '/' and char following */ + p--; + break; + } + } + putchar(c); + } + if (*ap) + putchar(' '); + } + if (! nflag) + putchar('\n'); + return 0; +} + +rtems_shell_cmd_t rtems_shell_ECHO_Command = { + "echo", /* name */ + "echo [args]", /* usage */ + "misc", /* topic */ + rtems_shell_main_echo, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_exit.c b/cpukit/libmisc/shell/main_exit.c new file mode 100644 index 0000000000..4d996cde10 --- /dev/null +++ b/cpukit/libmisc/shell/main_exit.c @@ -0,0 +1,26 @@ +/* + * exit Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +rtems_shell_alias_t rtems_shell_EXIT_Alias = { + "logoff", /* command */ + "exit" /* alias */ +}; diff --git a/cpukit/libmisc/shell/main_getenv.c b/cpukit/libmisc/shell/main_getenv.c new file mode 100644 index 0000000000..4e4789b632 --- /dev/null +++ b/cpukit/libmisc/shell/main_getenv.c @@ -0,0 +1,49 @@ +/* + * Get an environment vairable. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_getenv(int argc, char *argv[]) +{ + char* string; + + if (argc != 2) + { + printf ("error: only argument is the variable name\n"); + return 1; + } + + string = getenv (argv[1]); + + if (!string) + { + printf ("error: %s not found\n", argv[1]); + return 1; + } + + printf ("%s\n", string); + + return 0; +} + +rtems_shell_cmd_t rtems_shell_GETENV_Command = { + "getenv", /* name */ + "getenv [var]", /* usage */ + "misc", /* topic */ + rtems_shell_main_getenv, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_halt.c b/cpukit/libmisc/shell/main_halt.c new file mode 100644 index 0000000000..f6f9f7e4fd --- /dev/null +++ b/cpukit/libmisc/shell/main_halt.c @@ -0,0 +1,40 @@ +/* + * Halt Command Implementation + * + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_halt( + int argc __attribute__((unused)), + char *argv[] __attribute__((unused)) +) +{ + exit(0); + return 0; +} + +rtems_shell_cmd_t rtems_shell_HALT_Command = { + "halt", /* name */ + "halt", /* usage */ + "rtems", /* topic */ + rtems_shell_main_halt, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_help.c b/cpukit/libmisc/shell/main_help.c new file mode 100644 index 0000000000..512eb60bbc --- /dev/null +++ b/cpukit/libmisc/shell/main_help.c @@ -0,0 +1,152 @@ +/* + * + * Shell Help Command + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <time.h> + +#include <rtems.h> +#include <rtems/error.h> +#include <rtems/system.h> +#include <rtems/shell.h> + +#include "internal.h" +#include <string.h> + +/* + * show the help for one command. + */ +int rtems_shell_help_cmd( + rtems_shell_cmd_t *shell_cmd +) +{ + const char * pc; + int col,line; + + printf("%-12.12s - ",shell_cmd->name); + col = 14; + line = 1; + if (shell_cmd->alias) { + printf("is an <alias> for command '%s'",shell_cmd->alias->name); + } else if (shell_cmd->usage) { + pc = shell_cmd->usage; + while (*pc) { + switch(*pc) { + case '\r': + break; + case '\n': + putchar('\n'); + col = 0; + break; + default: + putchar(*pc); + col++; + break; + } + pc++; + if (col>78) { /* What daring... 78?*/ + if (*pc) { + putchar('\n'); + col = 0; + } + } + if (!col && *pc) { + printf(" "); + col = 12;line++; + } + } + } + puts(""); + return line; +} + +/* + * show the help. The first command implemented. + * Can you see the header of routine? Known? + * The same with all the commands.... + */ +int rtems_shell_help( + int argc, + char * argv[] +) +{ + int col,line,arg; + rtems_shell_topic_t *topic; + rtems_shell_cmd_t * shell_cmd = rtems_shell_first_cmd; + + if (argc<2) { + printf("help: ('r' repeat last cmd - 'e' edit last cmd)\n" + " TOPIC? The topics are\n"); + topic = rtems_shell_first_topic; + col = 0; + while (topic) { + if (!col){ + col = printf(" %s",topic->topic); + } else { + if ((col+strlen(topic->topic)+2)>78){ + printf("\n"); + col = printf(" %s",topic->topic); + } else { + col+= printf(", %s",topic->topic); + } + } + topic = topic->next; + } + printf("\n"); + return 1; + } + line = 0; + for (arg = 1;arg<argc;arg++) { + if (line>16) { + printf("Press any key to continue...");getchar(); + printf("\n"); + line = 0; + } + topic = rtems_shell_lookup_topic(argv[arg]); + if (!topic){ + if ((shell_cmd = rtems_shell_lookup_cmd(argv[arg])) == NULL) { + printf("help: topic or cmd '%s' not found. Try <help> alone for a list\n", + argv[arg]); + line++; + } else { + line+= rtems_shell_help_cmd(shell_cmd); + } + continue; + } + printf("help: list for the topic '%s'\n",argv[arg]); + line++; + while (shell_cmd) { + if (!strcmp(topic->topic,shell_cmd->topic)) + line+= rtems_shell_help_cmd(shell_cmd); + if (line>16) { + printf("Press any key to continue..."); + getchar(); + printf("\n"); + line = 0; + } + shell_cmd = shell_cmd->next; + } + } + puts(""); + return 0; +} + +rtems_shell_cmd_t rtems_shell_HELP_Command = { + "help", /* name */ + "help [topic] # list of usage of commands", /* usage */ + "help", /* topic */ + rtems_shell_help, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_hexdump.c b/cpukit/libmisc/shell/main_hexdump.c new file mode 100644 index 0000000000..8eb5a4e409 --- /dev/null +++ b/cpukit/libmisc/shell/main_hexdump.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)hexdump.c 8.1 (Berkeley) 6/6/93"; +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/hexdump/hexdump.c,v 1.7 2002/09/04 23:29:01 dwmalone Exp $"); +#endif +#endif /* not lint */ + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/shellconfig.h> + +#include <sys/types.h> +#include <locale.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define rindex(s,c) strrchr(s,c) + +#include "hexdump.h" + +#if RTEMS_REMOVED +FS *fshead; /* head of format strings */ +int blocksize; /* data block size */ +int exitval; /* final exit value */ +int length = -1; /* max bytes to read */ +#endif + +void +rtems_shell_hexdump_exit (rtems_shell_hexdump_globals* globals, int code) +{ + globals->exit_code = code; + longjmp (globals->exit_jmp, 1); +} + +static int main_hexdump(rtems_shell_hexdump_globals* globals, int argc, char *argv[]); + +int +rtems_shell_main_hexdump(int argc, char *argv[]) +{ + rtems_shell_hexdump_globals hexdump_globals; + rtems_shell_hexdump_globals* globals = &hexdump_globals; + memset (globals, 0, sizeof (hexdump_globals)); + vflag = FIRST; + ateof = 1; + hexdump_globals.exit_code = 1; + if (setjmp (hexdump_globals.exit_jmp) == 0) + hexdump_globals.exit_code = main_hexdump (globals, argc, argv); + if (curp) + free (curp); + if (savp) + free (savp); + while (fshead) + { + FS* nextfs = fshead->nextfs; + while (fshead->nextfu) + { + FU* nextfu = fshead->nextfu->nextfu; + if (fshead->nextfu->fmt) + free(fshead->nextfu->fmt); + while (fshead->nextfu->nextpr) + { + PR* nextpr = fshead->nextfu->nextpr->nextpr; + if (((fshead->nextfu->nextpr->flags & F_TEXT) == 0) && + fshead->nextfu->nextpr->fmt) + free(fshead->nextfu->nextpr->fmt); + free(fshead->nextfu->nextpr); + fshead->nextfu->nextpr = nextpr; + } + free(fshead->nextfu); + fshead->nextfu = nextfu; + } + free(fshead); + fshead = nextfs; + } + if (hdstdin) + { + fclose (hdstdin); + free (hdstdin); + } + return hexdump_globals.exit_code; +} + +int +main_hexdump(rtems_shell_hexdump_globals* globals, int argc, char *argv[]) +{ + FS *tfs; + char *p; + +#if RTEMS_REMOVED + (void)setlocale(LC_ALL, ""); +#endif + + if (!(p = rindex(argv[0], 'o')) || strcmp(p, "od")) + newsyntax(globals, argc, &argv); + else + oldsyntax(globals, argc, &argv); + + /* figure out the data block size */ + for (blocksize = 0, tfs = fshead; tfs; tfs = tfs->nextfs) { + tfs->bcnt = size(globals, tfs); + if (blocksize < tfs->bcnt) + blocksize = tfs->bcnt; + } + /* rewrite the rules, do syntax checking */ + for (tfs = fshead; tfs; tfs = tfs->nextfs) + rewrite(globals, tfs); + + (void)next(globals, argv); + display(globals); + exit(exitval); + return exitval; +} + +rtems_shell_cmd_t rtems_shell_HEXDUMP_Command = { + "hexdump", /* name */ + "hexdump [-bcCdovx] [-e fmt] [-f fmt_file] [-n length]\n" /* usage */ + " [-s skip] [file ...]", + "files", /* topic */ + rtems_shell_main_hexdump, /* command */ + NULL, /* alias */ + NULL /* next */ +}; + diff --git a/cpukit/libmisc/shell/main_id.c b/cpukit/libmisc/shell/main_id.c new file mode 100644 index 0000000000..8fa0914597 --- /dev/null +++ b/cpukit/libmisc/shell/main_id.c @@ -0,0 +1,67 @@ +/* + * ID Command Implementation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <pwd.h> +#include <grp.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_id( + int argc __attribute__((unused)), + char *argv[] __attribute__((unused)) +) +{ + struct passwd *pwd; + struct group *grp; + + pwd = getpwuid(getuid()); + grp = getgrgid(getgid()); + printf( + "uid=%d(%s),gid=%d(%s),", + getuid(), + (pwd) ? pwd->pw_name : "", + getgid(), + (grp) ? grp->gr_name : "" + ); + pwd = getpwuid(geteuid()); + grp = getgrgid(getegid()); + printf( + "euid=%d(%s),egid=%d(%s)\n", + geteuid(), + (pwd) ? pwd->pw_name : "", + getegid(), + (grp) ? grp->gr_name : "" + ); + return 0; +} + +rtems_shell_cmd_t rtems_shell_ID_Command = { + "id", /* name */ + "show uid, gid, euid, and egid", /* usage */ + "misc", /* topic */ + rtems_shell_main_id, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_ifconfig.c b/cpukit/libmisc/shell/main_ifconfig.c new file mode 100644 index 0000000000..9f16207b10 --- /dev/null +++ b/cpukit/libmisc/shell/main_ifconfig.c @@ -0,0 +1,243 @@ +/* + * IFCONFIG Command Implmentation + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> + +#include <netinet/in.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <net/if.h> + + +#include <rtems.h> +#include <rtems/rtems_bsdnet.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_ifconfig( + int argc, + char *argv[] +) +{ + struct sockaddr_in ipaddr; + struct sockaddr_in dstaddr; + struct sockaddr_in netmask; + struct sockaddr_in broadcast; + char *iface; + int f_ip = 0; + int f_ptp = 0; + int f_netmask = 0; + int f_up = 0; + int f_down = 0; + int f_bcast = 0; + int cur_idx; + int rc; + int flags; + + memset(&ipaddr, 0, sizeof(ipaddr)); + memset(&dstaddr, 0, sizeof(dstaddr)); + memset(&netmask, 0, sizeof(netmask)); + memset(&broadcast, 0, sizeof(broadcast)); + + ipaddr.sin_len = sizeof(ipaddr); + ipaddr.sin_family = AF_INET; + + dstaddr.sin_len = sizeof(dstaddr); + dstaddr.sin_family = AF_INET; + + netmask.sin_len = sizeof(netmask); + netmask.sin_family = AF_INET; + + broadcast.sin_len = sizeof(broadcast); + broadcast.sin_family = AF_INET; + + cur_idx = 0; + if (argc <= 1) { + /* display all interfaces */ + iface = NULL; + cur_idx += 1; + } else { + iface = argv[1]; + if (isdigit((unsigned char)*argv[2])) { + if (inet_pton(AF_INET, argv[2], &ipaddr.sin_addr) < 0) { + printf("bad ip address: %s\n", argv[2]); + return 0; + } + f_ip = 1; + cur_idx += 3; + } else { + cur_idx += 2; + } + } + + if ((f_down !=0) && (f_ip != 0)) { + f_up = 1; + } + + while(argc > cur_idx) { + if (strcmp(argv[cur_idx], "up") == 0) { + f_up = 1; + if (f_down != 0) { + printf("Can't make interface up and down\n"); + } + } else if(strcmp(argv[cur_idx], "down") == 0) { + f_down = 1; + if (f_up != 0) { + printf("Can't make interface up and down\n"); + } + } else if(strcmp(argv[cur_idx], "netmask") == 0) { + if ((cur_idx + 1) >= argc) { + printf("No netmask address\n"); + return -1; + } + if (inet_pton(AF_INET, argv[cur_idx+1], &netmask.sin_addr) < 0) { + printf("bad netmask: %s\n", argv[cur_idx]); + return -1; + } + f_netmask = 1; + cur_idx += 1; + } else if(strcmp(argv[cur_idx], "broadcast") == 0) { + if ((cur_idx + 1) >= argc) { + printf("No broadcast address\n"); + return -1; + } + if (inet_pton(AF_INET, argv[cur_idx+1], &broadcast.sin_addr) < 0) { + printf("bad broadcast: %s\n", argv[cur_idx]); + return -1; + } + f_bcast = 1; + cur_idx += 1; + } else if(strcmp(argv[cur_idx], "pointopoint") == 0) { + if ((cur_idx + 1) >= argc) { + printf("No pointopoint address\n"); + return -1; + } + if (inet_pton(AF_INET, argv[cur_idx+1], &dstaddr.sin_addr) < 0) { + printf("bad pointopoint: %s\n", argv[cur_idx]); + return -1; + } + f_ptp = 1; + cur_idx += 1; + } else { + printf("Bad parameter: %s\n", argv[cur_idx]); + return -1; + } + cur_idx += 1; + } + + printf("ifconfig "); + if (iface != NULL) { + printf("%s ", iface); + if (f_ip != 0) { + char str[256]; + inet_ntop(AF_INET, &ipaddr.sin_addr, str, 256); + printf("%s ", str); + } + + if (f_netmask != 0) { + char str[256]; + inet_ntop(AF_INET, &netmask.sin_addr, str, 256); + printf("netmask %s ", str); + } + + if (f_bcast != 0) { + char str[256]; + inet_ntop(AF_INET, &broadcast.sin_addr, str, 256); + printf("broadcast %s ", str); + } + + if (f_ptp != 0) { + char str[256]; + inet_ntop(AF_INET, &dstaddr.sin_addr, str, 256); + printf("pointopoint %s ", str); + } + + if (f_up != 0) { + printf("up\n"); + } else if (f_down != 0) { + printf("down\n"); + } else { + printf("\n"); + } + } + + if ((iface == NULL) || ((f_ip == 0) && (f_down == 0) && (f_up == 0))) { + rtems_bsdnet_show_if_stats(); + return 0; + } + + flags = 0; + if (f_netmask) { + rc = rtems_bsdnet_ifconfig(iface, SIOCSIFNETMASK, &netmask); + if (rc < 0) { + printf("Could not set netmask: %s\n", strerror(errno)); + return -1; + } + } + + if (f_bcast) { + rc = rtems_bsdnet_ifconfig(iface, SIOCSIFBRDADDR, &broadcast); + if (rc < 0) { + printf("Could not set broadcast: %s\n", strerror(errno)); + return -1; + } + } + + if (f_ptp) { + rc = rtems_bsdnet_ifconfig(iface, SIOCSIFDSTADDR, &dstaddr); + if (rc < 0) { + printf("Could not set destination address: %s\n", strerror(errno)); + return -1; + } + flags |= IFF_POINTOPOINT; + } + + /* This must come _after_ setting the netmask, broadcast addresses */ + if (f_ip) { + rc = rtems_bsdnet_ifconfig(iface, SIOCSIFADDR, &ipaddr); + if (rc < 0) { + printf("Could not set IP address: %s\n", strerror(errno)); + return -1; + } + } + + if (f_up != 0) { + flags |= IFF_UP; + } + + if (f_down != 0) { + printf("Warning: taking interfaces down is not supported\n"); + } + + rc = rtems_bsdnet_ifconfig(iface, SIOCSIFFLAGS, &flags); + if (rc < 0) { + printf("Could not set interface flags: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +rtems_shell_cmd_t rtems_shell_IFCONFIG_Command = { + "ifconfig", /* name */ + "TBD", /* usage */ + "network", /* topic */ + rtems_shell_main_ifconfig, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_ln.c b/cpukit/libmisc/shell/main_ln.c new file mode 100644 index 0000000000..aff1ffbf91 --- /dev/null +++ b/cpukit/libmisc/shell/main_ln.c @@ -0,0 +1,309 @@ +/* $NetBSD: ln.c,v 1.34 2008/07/20 00:52:40 lukem Exp $ */ + +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if 0 +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ln.c 8.2 (Berkeley) 3/31/94"; +#else +__RCSID("$NetBSD: ln.c,v 1.34 2008/07/20 00:52:40 lukem Exp $"); +#endif +#endif /* not lint */ +#endif + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/shellconfig.h> +#define __need_getopt_newlib +#include <getopt.h> + +#include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <setjmp.h> + +typedef struct { + int fflag; /* Unlink existing files. */ + int hflag; /* Check new name for symlink first. */ + int iflag; /* Interactive mode. */ + int sflag; /* Symbolic, not hard, link. */ + int vflag; /* Verbose output */ + char linkch; + + int exit_code; + jmp_buf exit_jmp; +} rtems_shell_ln_globals; + +#define fflag globals->fflag +#define hflag globals->hflag +#define iflag globals->iflag +#define sflag globals->sflag +#define vflag globals->vflag +#define linkch globals->linkch + +#define exit_jump &(globals->exit_jmp) + +#define exit(ec) rtems_shell_ln_exit(globals, ec) + +static int main_ln(rtems_shell_ln_globals* , int, char *[]); + +static void +rtems_shell_ln_exit (rtems_shell_ln_globals* globals, int code) +{ + globals->exit_code = code; + longjmp (globals->exit_jmp, 1); +} + +static int main_ln(rtems_shell_ln_globals* globals, int argc, char *argv[]); + +int +rtems_shell_main_ln(int argc, char *argv[]) +{ + rtems_shell_ln_globals ln_globals; + rtems_shell_ln_globals* globals = &ln_globals; + memset (globals, 0, sizeof (ln_globals)); + ln_globals.exit_code = 1; + if (setjmp (ln_globals.exit_jmp) == 0) + return main_ln (globals, argc, argv); + return ln_globals.exit_code; +} + +#if RTEMS_REMOVED +int fflag; /* Unlink existing files. */ +int hflag; /* Check new name for symlink first. */ +int iflag; /* Interactive mode. */ +int sflag; /* Symbolic, not hard, link. */ +int vflag; /* Verbose output */ + + /* System link call. */ +int (*linkf)(const char *, const char *); +char linkch; +#endif + +int (*linkf)(const char *, const char *); + +static int linkit(rtems_shell_ln_globals* , const char *, const char *, int); +static void usage(rtems_shell_ln_globals* ); + +static int +main_ln(rtems_shell_ln_globals* globals, int argc, char *argv[]) +{ + struct stat sb; + int ch, exitval; + char *sourcedir; + + struct getopt_data getopt_reent; + memset(&getopt_reent, 0, sizeof(getopt_data)); + +#if RTEMS_REMOVED + setprogname(argv[0]); + (void)setlocale(LC_ALL, ""); +#endif + + while ((ch = getopt_r(argc, argv, "fhinsv", &getopt_reent)) != -1) + switch (ch) { + case 'f': + fflag = 1; + iflag = 0; + break; + case 'h': + case 'n': + hflag = 1; + break; + case 'i': + iflag = 1; + fflag = 0; + break; + case 's': + sflag = 1; + break; + case 'v': + vflag = 1; + break; + case '?': + default: + usage(globals); + /* NOTREACHED */ + } + + argv += getopt_reent.optind; + argc -= getopt_reent.optind; + + if (sflag) { + linkf = symlink; + linkch = '-'; + } else { + linkf = link; + linkch = '='; + } + + switch(argc) { + case 0: + usage(globals); + /* NOTREACHED */ + case 1: /* ln target */ + exit(linkit(globals, argv[0], ".", 1)); + /* NOTREACHED */ + case 2: /* ln target source */ + exit(linkit(globals, argv[0], argv[1], 0)); + /* NOTREACHED */ + } + + /* ln target1 target2 directory */ + sourcedir = argv[argc - 1]; + if (hflag && lstat(sourcedir, &sb) == 0 && S_ISLNK(sb.st_mode)) { + /* we were asked not to follow symlinks, but found one at + the target--simulate "not a directory" error */ + errno = ENOTDIR; + err(exit_jump, EXIT_FAILURE, "%s", sourcedir); + /* NOTREACHED */ + } + if (stat(sourcedir, &sb)) { + err(exit_jump, EXIT_FAILURE, "%s", sourcedir); + /* NOTREACHED */ + } + if (!S_ISDIR(sb.st_mode)) { + usage(globals); + /* NOTREACHED */ + } + for (exitval = 0; *argv != sourcedir; ++argv) + exitval |= linkit(globals, *argv, sourcedir, 1); + exit(exitval); + /* NOTREACHED */ + return 0; +} + +int +linkit(rtems_shell_ln_globals* globals, const char *source, const char *target, int isdir) +{ + struct stat sb; + const char *p; + char path[MAXPATHLEN]; + int ch, exists, first; + + if (!sflag) { + /* If target doesn't exist, quit now. */ + if (stat(target, &sb)) { + warn("%s", target); + return (1); + } + } + + /* If the source is a directory (and not a symlink if hflag), + append the target's name. */ + if (isdir || + (!lstat(source, &sb) && S_ISDIR(sb.st_mode)) || + (!hflag && !stat(source, &sb) && S_ISDIR(sb.st_mode))) { + if ((p = strrchr(target, '/')) == NULL) + p = target; + else + ++p; + (void)snprintf(path, sizeof(path), "%s/%s", source, p); + source = path; + } + + exists = !lstat(source, &sb); + + /* + * If the file exists, then unlink it forcibly if -f was specified + * and interactively if -i was specified. + */ + if (fflag && exists) { + if (unlink(source)) { + warn("%s", source); + return (1); + } + } else if (iflag && exists) { + fflush(stdout); + (void)fprintf(stderr, "replace %s? ", source); + + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (first != 'y' && first != 'Y') { + (void)fprintf(stderr, "not replaced\n"); + return (1); + } + + if (unlink(source)) { + warn("%s", source); + return (1); + } + } + + /* Attempt the link. */ + if ((*linkf)(target, source)) { + warn("%s", source); + return (1); + } + if (vflag) + (void)printf("%s %c> %s\n", source, linkch, target); + + return (0); +} + +void +usage(rtems_shell_ln_globals* globals) +{ +#define getprogname() "ln" + (void)fprintf(stderr, + "usage:\t%s [-fhinsv] file1 file2\n\t%s [-fhinsv] file ... directory\n", + getprogname(), getprogname()); + exit(1); + /* NOTREACHED */ +} + +rtems_shell_cmd_t rtems_shell_LN_Command = { + "ln", /* name */ + "ln ln [-fhinsv] source_file [target_file]", /* usage */ + "files", /* topic */ + rtems_shell_main_ln, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_logoff.c b/cpukit/libmisc/shell/main_logoff.c new file mode 100644 index 0000000000..7258724f04 --- /dev/null +++ b/cpukit/libmisc/shell/main_logoff.c @@ -0,0 +1,44 @@ +/* + * LOGOFF Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_logoff( + int argc __attribute__((unused)), + char *argv[] __attribute__((unused)) +) +{ + printf("logoff from the system..."); + + rtems_current_shell_env->exit_shell = true; + + return 0; +} + +rtems_shell_cmd_t rtems_shell_LOGOFF_Command = { + "logoff", /* name */ + "logoff from the system", /* usage */ + "misc", /* topic */ + rtems_shell_main_logoff, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_ls.c b/cpukit/libmisc/shell/main_ls.c new file mode 100644 index 0000000000..08072fb723 --- /dev/null +++ b/cpukit/libmisc/shell/main_ls.c @@ -0,0 +1,776 @@ +/* $NetBSD: ls.c,v 1.58 2005/10/26 02:24:22 jschauma Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if 0 +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ls.c 8.7 (Berkeley) 8/5/94"; +#else +__RCSID("$NetBSD: ls.c,v 1.58 2005/10/26 02:24:22 jschauma Exp $"); +#endif +#endif /* not lint */ +#endif + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/shellconfig.h> +#define __need_getopt_newlib +#include <getopt.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <termios.h> +#include <pwd.h> +#include <grp.h> + +#include "extern-ls.h" + +static void display(rtems_shell_ls_globals* globals, FTSENT *, FTSENT *); +static int mastercmp_listdir(const FTSENT **, const FTSENT **); +static int mastercmp_no_listdir(const FTSENT **, const FTSENT **); +static void traverse(rtems_shell_ls_globals* globals, int, char **, int); + +static void (*printfcn)(rtems_shell_ls_globals* globals, DISPLAY *); +static int (*sortfcn)(const FTSENT *, const FTSENT *); + +#define BY_NAME 0 +#define BY_SIZE 1 +#define BY_TIME 2 + +#if RTEMS_REMOVED +long blocksize; /* block size units */ +int termwidth = 80; /* default terminal width */ +int sortkey = BY_NAME; +int rval = EXIT_SUCCESS; /* exit value - set if error encountered */ + +/* flags */ +int f_accesstime; /* use time of last access */ +int f_column; /* columnated format */ +int f_columnacross; /* columnated format, sorted across */ +int f_flags; /* show flags associated with a file */ +int f_grouponly; /* long listing without owner */ +int f_humanize; /* humanize the size field */ +int f_inode; /* print inode */ +int f_listdir; /* list actual directory, not contents */ +int f_listdot; /* list files beginning with . */ +int f_longform; /* long listing format */ +int f_nonprint; /* show unprintables as ? */ +int f_nosort; /* don't sort output */ +int f_numericonly; /* don't convert uid/gid to name */ +int f_octal; /* print octal escapes for nongraphic characters */ +int f_octal_escape; /* like f_octal but use C escapes if possible */ +int f_recursive; /* ls subdirectories also */ +int f_reversesort; /* reverse whatever sort is used */ +int f_sectime; /* print the real time for all files */ +int f_singlecol; /* use single column output */ +int f_size; /* list size in short listing */ +int f_statustime; /* use time of last mode change */ +int f_stream; /* stream format */ +int f_type; /* add type character for non-regular files */ +int f_typedir; /* add type character for directories */ +int f_whiteout; /* show whiteout entries */ +#endif + +void +rtems_shell_ls_exit (rtems_shell_ls_globals* globals, int code) +{ + globals->exit_code = code; + longjmp (globals->exit_jmp, 1); +} + +static int main_ls(rtems_shell_ls_globals* globals, int argc, char *argv[]); + +int +rtems_shell_main_ls(int argc, char *argv[]) +{ + rtems_shell_ls_globals ls_globals; + rtems_shell_ls_globals* globals = &ls_globals; + memset (globals, 0, sizeof (ls_globals)); + termwidth = 80; + sortkey = BY_NAME; + rval = EXIT_SUCCESS; + ls_globals.exit_code = 1; + if (setjmp (ls_globals.exit_jmp) == 0) + return main_ls (globals, argc, argv); + return ls_globals.exit_code; +} + +int +main_ls(rtems_shell_ls_globals* globals, int argc, char *argv[]) +{ + static char dot[] = ".", *dotav[] = { dot, NULL }; + //struct winsize win; + int ch, fts_options; + int kflag = 0; + const char *p; + + struct getopt_data getopt_reent; + memset(&getopt_reent, 0, sizeof(getopt_data)); + +#if RTEMS_REMOVED + setprogname(argv[0]); +#endif + setlocale(LC_ALL, ""); + + /* Terminal defaults to -Cq, non-terminal defaults to -1. */ + if (isatty(STDOUT_FILENO)) { +#if RTEMS_REMOVED + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && + win.ws_col > 0) + termwidth = win.ws_col; + f_column = f_nonprint = 1; +#endif + } else + f_singlecol = 1; + + /* Root is -A automatically. */ + if (!getuid()) + f_listdot = 1; + + fts_options = FTS_PHYSICAL; + while ((ch = getopt_r(argc, argv, + "1ABCFLRSTWabcdfghiklmnopqrstuwx", &getopt_reent)) != -1) { + switch (ch) { + /* + * The -1, -C, -l, -m and -x options all override each other so + * shell aliasing works correctly. + */ + case '1': + f_singlecol = 1; + f_column = f_columnacross = f_longform = f_stream = 0; + break; + case 'C': + f_column = 1; + f_columnacross = f_longform = f_singlecol = f_stream = + 0; + break; + case 'g': + if (f_grouponly != -1) + f_grouponly = 1; + f_longform = 1; + f_column = f_columnacross = f_singlecol = f_stream = 0; + break; + case 'l': + f_longform = 1; + f_column = f_columnacross = f_singlecol = f_stream = 0; + /* Never let -g take precedence over -l. */ + f_grouponly = -1; + break; + case 'm': + f_stream = 1; + f_column = f_columnacross = f_longform = f_singlecol = + 0; + break; + case 'x': + f_columnacross = 1; + f_column = f_longform = f_singlecol = f_stream = 0; + break; + /* The -c and -u options override each other. */ + case 'c': + f_statustime = 1; + f_accesstime = 0; + break; + case 'u': + f_accesstime = 1; + f_statustime = 0; + break; + case 'F': + f_type = 1; + break; + case 'L': + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + break; + case 'R': + f_recursive = 1; + break; + case 'a': + fts_options |= FTS_SEEDOT; + /* FALLTHROUGH */ + case 'A': + f_listdot = 1; + break; + /* The -B option turns off the -b, -q and -w options. */ + case 'B': + f_nonprint = 0; + f_octal = 1; + f_octal_escape = 0; + break; + /* The -b option turns off the -B, -q and -w options. */ + case 'b': + f_nonprint = 0; + f_octal = 0; + f_octal_escape = 1; + break; + /* The -d option turns off the -R option. */ + case 'd': + f_listdir = 1; + f_recursive = 0; + break; + case 'f': + f_nosort = 1; + break; + case 'i': + f_inode = 1; + break; + case 'k': + blocksize = 1024; + kflag = 1; + break; + /* The -h option forces all sizes to be measured in bytes. */ + case 'h': + f_humanize = 1; + break; + case 'n': + f_numericonly = 1; + break; + case 'o': + f_flags = 1; + break; + case 'p': + f_typedir = 1; + break; + /* The -q option turns off the -B, -b and -w options. */ + case 'q': + f_nonprint = 1; + f_octal = 0; + f_octal_escape = 0; + break; + case 'r': + f_reversesort = 1; + break; + case 'S': + sortkey = BY_SIZE; + break; + case 's': + f_size = 1; + break; + case 'T': + f_sectime = 1; + break; + case 't': + sortkey = BY_TIME; + break; + case 'W': + f_whiteout = 1; + break; + /* The -w option turns off the -B, -b and -q options. */ + case 'w': + f_nonprint = 0; + f_octal = 0; + f_octal_escape = 0; + break; + default: + case '?': + usage(globals); + } + } + argc -= getopt_reent.optind; + argv += getopt_reent.optind; + + if (f_column || f_columnacross || f_stream) { + if ((p = getenv("COLUMNS")) != NULL) + termwidth = atoi(p); + } + + /* + * If both -g and -l options, let -l take precedence. + */ + if (f_grouponly == -1) + f_grouponly = 0; + + /* + * If not -F, -i, -l, -p, -S, -s or -t options, don't require stat + * information. + */ + if (!f_inode && !f_longform && !f_size && !f_type && !f_typedir && + sortkey == BY_NAME) + fts_options |= FTS_NOSTAT; + + /* + * If not -F, -d or -l options, follow any symbolic links listed on + * the command line. + */ + if (!f_longform && !f_listdir && !f_type) + fts_options |= FTS_COMFOLLOW; + + /* + * If -W, show whiteout entries + */ +#ifdef FTS_WHITEOUT + if (f_whiteout) + fts_options |= FTS_WHITEOUT; +#endif + + /* If -l or -s, figure out block size. */ + if (f_inode || f_longform || f_size) { +#if RTEMS_REMOVED + if (!kflag) + (void)getbsize(NULL, &blocksize); +#else + /* Make equal to 1 so ls -l shows the actual blcok count */ + blocksize = 512; +#endif + blocksize /= 512; + } + + /* Select a sort function. */ + if (f_reversesort) { + switch (sortkey) { + case BY_NAME: + sortfcn = revnamecmp; + break; + case BY_SIZE: + sortfcn = revsizecmp; + break; + case BY_TIME: + if (f_accesstime) + sortfcn = revacccmp; + else if (f_statustime) + sortfcn = revstatcmp; + else /* Use modification time. */ + sortfcn = revmodcmp; + break; + } + } else { + switch (sortkey) { + case BY_NAME: + sortfcn = namecmp; + break; + case BY_SIZE: + sortfcn = sizecmp; + break; + case BY_TIME: + if (f_accesstime) + sortfcn = acccmp; + else if (f_statustime) + sortfcn = statcmp; + else /* Use modification time. */ + sortfcn = modcmp; + break; + } + } + + /* Select a print function. */ + if (f_singlecol) + printfcn = printscol; + else if (f_columnacross) + printfcn = printacol; + else if (f_longform) + printfcn = printlong; + else if (f_stream) + printfcn = printstream; + else + printfcn = printcol; + + if (argc) + traverse(globals, argc, argv, fts_options); + else + traverse(globals, 1, dotav, fts_options); + exit(rval); + /* NOTREACHED */ + return 0; +} + +#if RTEMS_REMOVED +static int output; /* If anything output. */ +#endif + +/* + * Traverse() walks the logical directory structure specified by the argv list + * in the order specified by the mastercmp() comparison function. During the + * traversal it passes linked lists of structures to display() which represent + * a superset (may be exact set) of the files to be displayed. + */ +static void +traverse(rtems_shell_ls_globals* globals, int argc, char *argv[], int options) +{ + FTS *ftsp; + FTSENT *p, *chp; + int ch_options; + + if ((ftsp = + fts_open(argv, options, + f_nosort ? NULL : f_listdir ? + mastercmp_listdir : mastercmp_no_listdir)) == NULL) + err(exit_jump, EXIT_FAILURE, NULL); + + display(globals, NULL, fts_children(ftsp, 0)); + if (f_listdir) + { + fts_close(ftsp); + return; + } + + /* + * If not recursing down this tree and don't need stat info, just get + * the names. + */ + ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; + + while ((p = fts_read(ftsp)) != NULL) + switch (p->fts_info) { + case FTS_DC: + warnx("%s: directory causes a cycle", p->fts_name); + break; + case FTS_DNR: + case FTS_ERR: + warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); + rval = EXIT_FAILURE; + break; + case FTS_D: + if (p->fts_level != FTS_ROOTLEVEL && + p->fts_name[0] == '.' && !f_listdot) + break; + + /* + * If already output something, put out a newline as + * a separator. If multiple arguments, precede each + * directory with its name. + */ + if (output) + (void)printf("\n%s:\n", p->fts_path); + else if (argc > 1) { + (void)printf("%s:\n", p->fts_path); + output = 1; + } + + chp = fts_children(ftsp, ch_options); + display(globals, p, chp); + + if (!f_recursive && chp != NULL) + (void)fts_set(ftsp, p, FTS_SKIP); + break; + } + fts_close(ftsp); + if (errno) + err(exit_jump, EXIT_FAILURE, "fts_read"); +} + +/* + * Display() takes a linked list of FTSENT structures and passes the list + * along with any other necessary information to the print function. P + * points to the parent directory of the display list. + */ +static void +display(rtems_shell_ls_globals* globals, FTSENT *p, FTSENT *list) +{ + struct stat *sp; + DISPLAY d; + FTSENT *cur; + NAMES *np; + u_int64_t btotal, stotal, maxblock, maxsize; + int maxinode, maxnlink, maxmajor, maxminor; + int bcfile, entries, flen, glen, ulen, maxflags, maxgroup, maxlen; + int maxuser, needstats; + const char *user, *group; + char buf[21]; /* 64 bits == 20 digits, +1 for NUL */ + char nuser[12], ngroup[12]; + char *flags = NULL; + +#ifdef __GNUC__ + /* This outrageous construct just to shut up a GCC warning. */ + (void) &maxsize; +#endif + + /* + * If list is NULL there are two possibilities: that the parent + * directory p has no children, or that fts_children() returned an + * error. We ignore the error case since it will be replicated + * on the next call to fts_read() on the post-order visit to the + * directory p, and will be signalled in traverse(). + */ + if (list == NULL) + return; + + needstats = f_inode || f_longform || f_size; + flen = 0; + maxinode = maxnlink = 0; + bcfile = 0; + maxuser = maxgroup = maxflags = maxlen = 0; + btotal = stotal = maxblock = maxsize = 0; + maxmajor = maxminor = 0; + for (cur = list, entries = 0; cur; cur = cur->fts_link) { + uint64_t size; + if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { + warnx("%s: %s", + cur->fts_name, strerror(cur->fts_errno)); + cur->fts_number = NO_PRINT; + rval = EXIT_FAILURE; + continue; + } + + /* + * P is NULL if list is the argv list, to which different rules + * apply. + */ + if (p == NULL) { + /* Directories will be displayed later. */ + if (cur->fts_info == FTS_D && !f_listdir) { + cur->fts_number = NO_PRINT; + continue; + } + } else { + /* Only display dot file if -a/-A set. */ + if (cur->fts_name[0] == '.' && !f_listdot) { + cur->fts_number = NO_PRINT; + continue; + } + } + if (cur->fts_namelen > maxlen) + maxlen = cur->fts_namelen; + if (needstats) { + sp = cur->fts_statp; + if (sp->st_size < 0) + size = sp->st_size * -1; + else + size = sp->st_size; + if (sp->st_blocks > maxblock) + maxblock = sp->st_blocks; + if (sp->st_ino > maxinode) + maxinode = sp->st_ino; + if (sp->st_nlink > maxnlink) + maxnlink = sp->st_nlink; + if (size > maxsize) + maxsize = size; + if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) { + bcfile = 1; + if (major(sp->st_rdev) > maxmajor) + maxmajor = major(sp->st_rdev); + if (minor(sp->st_rdev) > maxminor) + maxminor = minor(sp->st_rdev); + } + + btotal += sp->st_blocks; + stotal += size; + if (f_longform) { + if (f_numericonly || + (user = user_from_uid(sp->st_uid, 0)) == + NULL) { + (void)snprintf(nuser, sizeof(nuser), + "%u", sp->st_uid); + user = nuser; + } + if (f_numericonly || + (group = group_from_gid(sp->st_gid, 0)) == + NULL) { + (void)snprintf(ngroup, sizeof(ngroup), + "%u", sp->st_gid); + group = ngroup; + } + if ((ulen = strlen(user)) > maxuser) + maxuser = ulen; + if ((glen = strlen(group)) > maxgroup) + maxgroup = glen; +#if RTEMS_REMOVED + if (f_flags) { + flags = + flags_to_string(sp->st_flags, "-"); + if ((flen = strlen(flags)) > maxflags) + maxflags = flen; + } else +#endif + flen = 0; + + if ((np = malloc(sizeof(NAMES) + + ulen + glen + flen + 3)) == NULL) + err(exit_jump, EXIT_FAILURE, NULL); + + np->user = &np->data[0]; + (void)strcpy(np->user, user); + np->group = &np->data[ulen + 1]; + (void)strcpy(np->group, group); + + if (f_flags && flags) { + np->flags = &np->data[ulen + glen + 2]; + (void)strcpy(np->flags, flags); + } + cur->fts_pointer = np; + } + } + ++entries; + } + + if (!entries) + return; + + d.list = list; + d.entries = entries; + d.maxlen = maxlen; + if (needstats) { + d.btotal = btotal; + d.stotal = stotal; + if (f_humanize) { + d.s_block = 4; /* min buf length for humanize_number */ + } else { + (void)snprintf(buf, sizeof(buf), "%llu", + (long long)howmany(maxblock, blocksize)); + d.s_block = strlen(buf); + } + d.s_flags = maxflags; + d.s_group = maxgroup; + (void)snprintf(buf, sizeof(buf), "%u", maxinode); + d.s_inode = strlen(buf); + (void)snprintf(buf, sizeof(buf), "%u", maxnlink); + d.s_nlink = strlen(buf); + if (f_humanize) { + d.s_size = 4; /* min buf length for humanize_number */ + } else { + (void)snprintf(buf, sizeof(buf), "%llu", + (long long)maxsize); + d.s_size = strlen(buf); + } + d.s_user = maxuser; + if (bcfile) { + (void)snprintf(buf, sizeof(buf), "%u", maxmajor); + d.s_major = strlen(buf); + (void)snprintf(buf, sizeof(buf), "%u", maxminor); + d.s_minor = strlen(buf); + if (d.s_major + d.s_minor + 2 > d.s_size) + d.s_size = d.s_major + d.s_minor + 2; + else if (d.s_size - d.s_minor - 2 > d.s_major) + d.s_major = d.s_size - d.s_minor - 2; + } else { + d.s_major = 0; + d.s_minor = 0; + } + } + + printfcn(globals, &d); + output = 1; + + if (f_longform) + for (cur = list; cur; cur = cur->fts_link) + free(cur->fts_pointer); +} + +/* + * Ordering for mastercmp: + * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories + * as larger than directories. Within either group, use the sort function. + * All other levels use the sort function. Error entries remain unsorted. + */ +static int +mastercmp_no_listdir(const FTSENT **a, const FTSENT **b) +{ + int a_info, b_info; + int l_f_listdir = 0; + + a_info = (*a)->fts_info; + if (a_info == FTS_ERR) + return (0); + b_info = (*b)->fts_info; + if (b_info == FTS_ERR) + return (0); + + if (a_info == FTS_NS || b_info == FTS_NS) { + if (b_info != FTS_NS) + return (1); + else if (a_info != FTS_NS) + return (-1); + else + return (namecmp(*a, *b)); + } + + if (a_info != b_info && !l_f_listdir && + (*a)->fts_level == FTS_ROOTLEVEL) { + if (a_info == FTS_D) + return (1); + else if (b_info == FTS_D) + return (-1); + } + return (sortfcn(*a, *b)); +} + +static int +mastercmp_listdir(const FTSENT **a, const FTSENT **b) +{ + int a_info, b_info; + int l_f_listdir = 1; + + a_info = (*a)->fts_info; + if (a_info == FTS_ERR) + return (0); + b_info = (*b)->fts_info; + if (b_info == FTS_ERR) + return (0); + + if (a_info == FTS_NS || b_info == FTS_NS) { + if (b_info != FTS_NS) + return (1); + else if (a_info != FTS_NS) + return (-1); + else + return (namecmp(*a, *b)); + } + + if (a_info != b_info && !l_f_listdir && + (*a)->fts_level == FTS_ROOTLEVEL) { + if (a_info == FTS_D) + return (1); + else if (b_info == FTS_D) + return (-1); + } + return (sortfcn(*a, *b)); +} + +rtems_shell_cmd_t rtems_shell_LS_Command = { + "ls", /* name */ + "ls [dir] # list files in the directory", /* usage */ + "files", /* topic */ + rtems_shell_main_ls, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_mallocinfo.c b/cpukit/libmisc/shell/main_mallocinfo.c new file mode 100644 index 0000000000..12423c7a73 --- /dev/null +++ b/cpukit/libmisc/shell/main_mallocinfo.c @@ -0,0 +1,63 @@ +/* + * MALLOC_INFO Shell Command Implmentation + * + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#include <rtems.h> +#include <rtems/malloc.h> +#include <rtems/shell.h> +#include "internal.h" + +extern int malloc_info( region_information_block * ); +extern void rtems_shell_print_unified_work_area_message(void); + +int rtems_shell_main_malloc_info( + int argc, + char *argv[] +) +{ + if ( argc == 2 ) { + rtems_shell_print_unified_work_area_message(); + + if ( !strcmp( argv[1], "info" ) ) { + region_information_block info; + + malloc_info( &info ); + rtems_shell_print_heap_info( "free", &info.Free ); + rtems_shell_print_heap_info( "used", &info.Used ); + return 0; + } else if ( !strcmp( argv[1], "stats" ) ) { + malloc_report_statistics_with_plugin( + stdout, + (rtems_printk_plugin_t) fprintf + ); + return 0; + } + } + fprintf( stderr, "%s: [info|stats]\n", argv[0] ); + return -1; +} + +rtems_shell_cmd_t rtems_shell_MALLOC_INFO_Command = { + "malloc", /* name */ + "[info|stats]", /* usage */ + "mem", /* topic */ + rtems_shell_main_malloc_info, /* command */ + NULL, /* alias */ + NULL /* next */ +}; + diff --git a/cpukit/libmisc/shell/main_mdump.c b/cpukit/libmisc/shell/main_mdump.c new file mode 100644 index 0000000000..d196711363 --- /dev/null +++ b/cpukit/libmisc/shell/main_mdump.c @@ -0,0 +1,96 @@ +/* + * MDUMP Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <ctype.h> +#include <stdio.h> +#include <inttypes.h> +#include <string.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/stringto.h> +#include "internal.h" + +int rtems_shell_main_mdump( + int argc, + char *argv[] +) +{ + unsigned char n; + unsigned char m; + int max; + int res; + void *addr = NULL; + unsigned char *pb; + + if (argc > 1) { + if ( rtems_string_to_pointer(argv[1], &addr, NULL) ) { + printf( "Address argument (%s) is not a number\n", argv[1] ); + return -1; + } + + } + + if (argc > 2) { + if ( rtems_string_to_int(argv[1], &max, NULL, 0) ) { + printf( "Length argument (%s) is not a number\n", argv[1] ); + return -1; + } + if (max <= 0) { + max = 1; /* print 1 item if 0 or neg. */ + res = 0; + } else { + max--; + res = max & 0xf;/* num bytes in last row */ + max >>= 4; /* div by 16 */ + max++; /* num of rows to print */ + if (max > 20) { /* limit to 20 */ + max = 20; + res = 0xf; /* 16 bytes print in last row */ + } + } + } else { + max = 20; + res = 0xf; + } + + pb = addr; + for (m=0; m<max; m++) { + printf("%10p ", pb); + for (n=0;n<=(m==(max-1)?res:0xf);n++) + printf("%02X%c",pb[n],n==7?'-':' '); + for (;n<=0xf;n++) + printf(" %c",n==7?'-':' '); + for (n=0;n<=(m==(max-1)?res:0xf);n++) { + printf("%c", isprint(pb[n]) ? pb[n] : '.'); + } + printf("\n"); + pb += 16; + } + return 0; +} + +rtems_shell_cmd_t rtems_shell_MDUMP_Command = { + "mdump", /* name */ + "mdump [address [length]]", /* usage */ + "mem", /* topic */ + rtems_shell_main_mdump, /* command */ + NULL, /* alias */ + NULL /* next */ +}; + diff --git a/cpukit/libmisc/shell/main_medit.c b/cpukit/libmisc/shell/main_medit.c new file mode 100644 index 0000000000..7905fbe689 --- /dev/null +++ b/cpukit/libmisc/shell/main_medit.c @@ -0,0 +1,79 @@ +/* + * MEDIT Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/stringto.h> +#include "internal.h" + +extern int rtems_shell_main_mdump(int, char *); + +int rtems_shell_main_medit( + int argc, + char *argv[] +) +{ + unsigned char *pb; + void *tmpp; + int n; + int i; + + if ( argc < 3 ) { + fprintf(stderr,"%s: too few arguments\n", argv[0]); + return -1; + } + + /* + * Convert arguments into numbers + */ + if ( rtems_string_to_pointer(argv[1], &tmpp, NULL) ) { + printf( "Address argument (%s) is not a number\n", argv[1] ); + return -1; + } + pb = tmpp; + + /* + * Now edit the memory + */ + n = 0; + for (i=2 ; i<=argc ; i++) { + unsigned char tmpc; + + if ( rtems_string_to_unsigned_char(argv[i], &tmpc, NULL, 0) ) { + printf( "Value (%s) is not a number\n", argv[i] ); + continue; + } + + pb[n++] = tmpc; + } + + return 0; +} + +rtems_shell_cmd_t rtems_shell_MEDIT_Command = { + "medit", /* name */ + "medit address value1 [value2 ...]", /* usage */ + "mem", /* topic */ + rtems_shell_main_medit, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_mfill.c b/cpukit/libmisc/shell/main_mfill.c new file mode 100644 index 0000000000..ecbaec4878 --- /dev/null +++ b/cpukit/libmisc/shell/main_mfill.c @@ -0,0 +1,77 @@ +/* + * MFILL Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/stringto.h> +#include "internal.h" + +int rtems_shell_main_mfill( + int argc, + char *argv[] +) +{ + unsigned long tmp; + void *addr; + size_t size; + unsigned char value; + + if ( argc != 4 ) { + fprintf(stderr,"%s: too few arguments\n", argv[0]); + return -1; + } + + /* + * Convert arguments into numbers + */ + if ( rtems_string_to_pointer(argv[1], &addr, NULL) ) { + printf( "Address argument (%s) is not a number\n", argv[1] ); + return -1; + } + + if ( rtems_string_to_unsigned_long(argv[2], &tmp, NULL, 0) ) { + printf( "Size argument (%s) is not a number\n", argv[2] ); + return -1; + } + size = (size_t) tmp; + + if ( rtems_string_to_unsigned_char(argv[3], &value, NULL, 0) ) { + printf( "Value argument (%s) is not a number\n", argv[3] ); + return -1; + } + + /* + * Now fill the memory. + */ + memset(addr, size, value); + + return 0; +} + +rtems_shell_cmd_t rtems_shell_MFILL_Command = { + "mfill", /* name */ + "mfill address size value", /* usage */ + "mem", /* topic */ + rtems_shell_main_mfill, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_mkdir.c b/cpukit/libmisc/shell/main_mkdir.c new file mode 100644 index 0000000000..e191818923 --- /dev/null +++ b/cpukit/libmisc/shell/main_mkdir.c @@ -0,0 +1,55 @@ +/* + * MKDIR Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_mkdir( + int argc, + char *argv[] +) +{ + char *dir; + int n; + + n = 1; + while (n<argc) { + dir = argv[n++]; + if (mkdir(dir,S_IRWXU|S_IRWXG|S_IRWXO)) { + fprintf(stderr, "mkdir '%s' failed:%s\n", dir, strerror(errno)); + } + } + return errno; +} + +rtems_shell_cmd_t rtems_shell_MKDIR_Command = { + "mkdir", /* name */ + "mkdir dir # make a directory", /* usage */ + "files", /* topic */ + rtems_shell_main_mkdir, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_mknod.c b/cpukit/libmisc/shell/main_mknod.c new file mode 100644 index 0000000000..e1ebb2a162 --- /dev/null +++ b/cpukit/libmisc/shell/main_mknod.c @@ -0,0 +1,463 @@ +/* $NetBSD: mknod.c,v 1.39 2009/02/13 01:37:23 lukem Exp $ */ + +/*- + * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#if 0 +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1998\ + The NetBSD Foundation, Inc. All rights reserved."); +__RCSID("$NetBSD: mknod.c,v 1.39 2009/02/13 01:37:23 lukem Exp $"); +#endif /* not lint */ +#endif + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/shellconfig.h> +#define __need_getopt_newlib +#include <getopt.h> + +#include <sys/cdefs.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#if !HAVE_NBTOOL_CONFIG_H && defined(KERN_DRIVERS) +#include <sys/sysctl.h> +#endif + +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <pwd.h> +#include <grp.h> +#include <string.h> +#include <ctype.h> + +#include "mknod-pack_dev.h" + +#include <setjmp.h> + +typedef struct { + int exit_code; + jmp_buf exit_jmp; +} rtems_shell_mknod_globals; + +#define exit_jump &(globals->exit_jmp) + +#define exit(ec) rtems_shell_mknod_exit(globals, ec) + +static int gid_name(const char *, gid_t *); +static portdev_t callPack(rtems_shell_mknod_globals* globals, + pack_t *, int, u_long *); + +static int main_mknod(rtems_shell_mknod_globals*, int, char *[]); +static void usage(rtems_shell_mknod_globals* ); + +static void +rtems_shell_mknod_exit (rtems_shell_mknod_globals* globals, int code) +{ + globals->exit_code = code; + longjmp (globals->exit_jmp, 1); +} + +#include "mknod-pack_dev.c" + +int +rtems_shell_main_mknod(int argc, char *argv[]) +{ + rtems_shell_mknod_globals mknod_globals; + rtems_shell_mknod_globals* globals = &mknod_globals; + memset (globals, 0, sizeof (mknod_globals)); + mknod_globals.exit_code = 1; + if (setjmp (mknod_globals.exit_jmp) == 0) + return main_mknod (globals, argc, argv); + return mknod_globals.exit_code; +} + +#define getprogname() "mknod" + +#ifdef KERN_DRIVERS +#error invalid for RTEMS +static struct kinfo_drivers *kern_drivers; +static int num_drivers; + +static void get_device_info(void); +static void print_device_info(char **); +static int major_from_name(const char *, mode_t); +#endif + +#define MAXARGS 3 /* 3 for bsdos, 2 for rest */ + +int +main_mknod(rtems_shell_mknod_globals* globals, int argc, char **argv) +{ + char *name, *p; + mode_t mode; + portdev_t dev; + pack_t *pack; + u_long numbers[MAXARGS]; + int n, ch, fifo, hasformat; + int r_flag = 0; /* force: delete existing entry */ +#ifdef KERN_DRIVERS + int l_flag = 0; /* list device names and numbers */ + int major; +#endif +#if RTEMS_REMOVED + void *modes = 0; +#endif + uid_t uid = -1; + gid_t gid = -1; + int rval; + + struct getopt_data getopt_reent; + memset(&getopt_reent, 0, sizeof(getopt_data)); + + dev = 0; + fifo = hasformat = 0; + pack = pack_native; + +#ifdef KERN_DRIVERS + while ((ch = getopt(argc, argv, "lrRF:g:m:u:")) != -1) { +#else + while ((ch = getopt_r(argc, argv, "rRF:g:m:u:", &getopt_reent)) != -1) { +#endif + switch (ch) { + +#ifdef KERN_DRIVERS + case 'l': + l_flag = 1; + break; +#endif + + case 'r': + r_flag = 1; + break; + + case 'R': + r_flag = 2; + break; + + case 'F': + pack = pack_find(getopt_reent.optarg); + if (pack == NULL) + errx(exit_jump, 1, "invalid format: %s", getopt_reent.optarg); + hasformat++; + break; + + case 'g': + if (getopt_reent.optarg[0] == '#') { + gid = strtol(getopt_reent.optarg + 1, &p, 10); + if (*p == 0) + break; + } + if (gid_name(getopt_reent.optarg, &gid) == 0) + break; + gid = strtol(getopt_reent.optarg, &p, 10); + if (*p == 0) + break; + errx(exit_jump, 1, "%s: invalid group name", getopt_reent.optarg); + + case 'm': +#if RTEMS_REMOVED + modes = setmode(getopt_reent.optarg); + if (modes == NULL) +#endif + err(exit_jump, 1, "Cannot set file mode `%s'", getopt_reent.optarg); + break; + + case 'u': + if (getopt_reent.optarg[0] == '#') { + uid = strtol(getopt_reent.optarg + 1, &p, 10); + if (*p == 0) + break; + } +#if RTEMS_REMOVED + if (uid_from_user(getopt_reent.optarg, &uid) == 0) + break; +#endif + uid = strtol(getopt_reent.optarg, &p, 10); + if (*p == 0) + break; + errx(exit_jump, 1, "%s: invalid user name", getopt_reent.optarg); + + default: + case '?': + usage(globals); + } + } + argc -= getopt_reent.optind; + argv += getopt_reent.optind; + +#ifdef KERN_DRIVERS + if (l_flag) { + print_device_info(argv); + return 0; + } +#endif + + if (argc < 2 || argc > 10) + usage(globals); + + name = *argv; + argc--; + argv++; + + umask(mode = umask(0)); + mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) & ~mode; + + if (argv[0][1] != '\0') + goto badtype; + switch (*argv[0]) { + case 'c': + mode |= S_IFCHR; + break; + + case 'b': + mode |= S_IFBLK; + break; + + case 'p': + if (hasformat) + errx(exit_jump, 1, "format is meaningless for fifos"); + mode |= S_IFIFO; + fifo = 1; + break; + + default: + badtype: + errx(exit_jump, 1, "node type must be 'b', 'c' or 'p'."); + } + argc--; + argv++; + + if (fifo) { + if (argc != 0) + usage(globals); + } else { + if (argc < 1 || argc > MAXARGS) + usage(globals); + } + + for (n = 0; n < argc; n++) { + errno = 0; + numbers[n] = strtoul(argv[n], &p, 0); + if (*p == 0 && errno == 0) + continue; +#ifdef KERN_DRIVERS + if (n == 0) { + major = major_from_name(argv[0], mode); + if (major != -1) { + numbers[0] = major; + continue; + } + if (!isdigit(*(unsigned char *)argv[0])) + errx(1, "unknown driver: %s", argv[0]); + } +#endif + errx(exit_jump, 1, "invalid number: %s", argv[n]); + } + + switch (argc) { + case 0: + dev = 0; + break; + + case 1: + dev = numbers[0]; + break; + + default: + dev = callPack(globals, pack, argc, numbers); + break; + } + +#if RTEMS_REMOVED + if (modes != NULL) + mode = getmode(modes, mode); +#endif + umask(0); + rval = fifo ? mkfifo(name, mode) : mknod(name, mode, dev); + if (rval < 0 && errno == EEXIST && r_flag) { + struct stat sb; + if (lstat(name, &sb) != 0 || (!fifo && sb.st_rdev != dev)) + sb.st_mode = 0; + + if ((sb.st_mode & S_IFMT) == (mode & S_IFMT)) { + if (r_flag == 1) + /* Ignore permissions and user/group */ + return 0; + if (sb.st_mode != mode) + rval = chmod(name, mode); + else + rval = 0; + } else { + unlink(name); + rval = fifo ? mkfifo(name, mode) + : mknod(name, mode, dev); + } + } + if (rval < 0) + err(exit_jump, 1, "%s", name); + if ((uid != (uid_t)-1 || gid != (uid_t)-1) && chown(name, uid, gid) == -1) + /* XXX Should we unlink the files here? */ + warn("%s: uid/gid not changed", name); + + return 0; +} + +static void +usage(rtems_shell_mknod_globals* globals) +{ + const char *progname = getprogname(); + + (void)fprintf(stderr, + "usage: %s [-rR] [-F format] [-m mode] [-u user] [-g group]\n", + progname); + (void)fprintf(stderr, +#ifdef KERN_DRIVERS + " [ name [b | c] [major | driver] minor\n" +#else + " [ name [b | c] major minor\n" +#endif + " | name [b | c] major unit subunit\n" + " | name [b | c] number\n" + " | name p ]\n"); +#ifdef KERN_DRIVERS + (void)fprintf(stderr, " %s -l [driver] ...\n", progname); +#endif + exit(1); +} + +static int +gid_name(const char *name, gid_t *gid) +{ + struct group *g; + + g = getgrnam(name); + if (!g) + return -1; + *gid = g->gr_gid; + return 0; +} + +static portdev_t +callPack(rtems_shell_mknod_globals* globals, pack_t *f, int n, u_long *numbers) +{ + portdev_t d; + const char *error = NULL; + + d = (*f)(n, numbers, &error); + if (error != NULL) + errx(exit_jump, 1, "%s", error); + return d; +} + +#ifdef KERN_DRIVERS +static void +get_device_info(void) +{ + static int mib[2] = {CTL_KERN, KERN_DRIVERS}; + size_t len; + + if (sysctl(mib, 2, NULL, &len, NULL, 0) != 0) + err(1, "kern.drivers" ); + kern_drivers = malloc(len); + if (kern_drivers == NULL) + err(1, "malloc"); + if (sysctl(mib, 2, kern_drivers, &len, NULL, 0) != 0) + err(1, "kern.drivers" ); + + num_drivers = len / sizeof *kern_drivers; +} + +static void +print_device_info(char **names) +{ + int i; + struct kinfo_drivers *kd; + + if (kern_drivers == NULL) + get_device_info(); + + do { + kd = kern_drivers; + for (i = 0; i < num_drivers; kd++, i++) { + if (*names && strcmp(*names, kd->d_name)) + continue; + printf("%s", kd->d_name); + if (kd->d_cmajor != -1) + printf(" character major %d", kd->d_cmajor); + if (kd->d_bmajor != -1) + printf(" block major %d", kd->d_bmajor); + printf("\n"); + } + } while (*names && *++names); +} + +static int +major_from_name(const char *name, mode_t mode) +{ + int i; + struct kinfo_drivers *kd; + + if (kern_drivers == NULL) + get_device_info(); + + kd = kern_drivers; + for (i = 0; i < num_drivers; kd++, i++) { + if (strcmp(name, kd->d_name)) + continue; + if (S_ISCHR(mode)) + return kd->d_cmajor; + return kd->d_bmajor; + } + return -1; +} +#endif + +rtems_shell_cmd_t rtems_shell_MKNOD_Command = { + "mknod", /* name */ + "mknod mknod [-rR] [-F fmt] [-m mode] name [c | b] minor", /* usage */ + "files", /* topic */ + rtems_shell_main_mknod, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_mkrfs.c b/cpukit/libmisc/shell/main_mkrfs.c new file mode 100644 index 0000000000..726b575b03 --- /dev/null +++ b/cpukit/libmisc/shell/main_mkrfs.c @@ -0,0 +1,35 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/stringto.h> +#include <rtems/shellconfig.h> +#include <rtems/rtems-rfs-shell.h> +#include <rtems/fsmount.h> +#include "internal.h" + +#define OPTIONS "[-v] [-s blksz] [-b grpblk] [-i grpinode] [-I] [-o %inode]" + +rtems_shell_cmd_t rtems_shell_MKRFS_Command = { + "mkrfs", /* name */ + "mkrfs " OPTIONS " dev", /* usage */ + "files", /* topic */ + rtems_shell_rfs_format, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_mmove.c b/cpukit/libmisc/shell/main_mmove.c new file mode 100644 index 0000000000..30c6629547 --- /dev/null +++ b/cpukit/libmisc/shell/main_mmove.c @@ -0,0 +1,79 @@ +/* + * MMOVE Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/stringto.h> +#include "internal.h" + +extern int rtems_shell_main_mdump(int, char *); + +int rtems_shell_main_mmove( + int argc, + char *argv[] +) +{ + unsigned long tmp; + void *src; + void *dst; + size_t length; + + if ( argc < 4 ) { + fprintf(stderr,"%s: too few arguments\n", argv[0]); + return -1; + } + + /* + * Convert arguments into numbers + */ + if ( rtems_string_to_pointer(argv[1], &dst, NULL) ) { + printf( "Destination argument (%s) is not a number\n", argv[1] ); + return -1; + } + + if ( rtems_string_to_pointer(argv[2], &src, NULL) ) { + printf( "Source argument (%s) is not a number\n", argv[2] ); + return -1; + } + + if ( rtems_string_to_unsigned_long(argv[3], &tmp, NULL, 0) ) { + printf( "Length argument (%s) is not a number\n", argv[3] ); + return -1; + } + length = (size_t) tmp; + + /* + * Now copy the memory. + */ + memcpy(dst, src, length); + + return 0; +} + +rtems_shell_cmd_t rtems_shell_MMOVE_Command = { + "mmove", /* name */ + "mmove dst src length", /* usage */ + "mem", /* topic */ + rtems_shell_main_mmove, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_mount.c b/cpukit/libmisc/shell/main_mount.c new file mode 100644 index 0000000000..b192a1668d --- /dev/null +++ b/cpukit/libmisc/shell/main_mount.c @@ -0,0 +1,133 @@ +/* + * Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/shellconfig.h> +#include <rtems/libio.h> +#include "internal.h" + +static bool print_filesystem(const rtems_filesystem_table_t *entry, void *arg) +{ + printf("%s ", entry->type); + + return false; +} + +int rtems_shell_main_mount( + int argc, + char *argv[] +) +{ + rtems_filesystem_options_t options = RTEMS_FILESYSTEM_READ_WRITE; + char* type = NULL; + char* source = NULL; + char* target = NULL; + char* fsoptions = NULL; + int arg; + + for (arg = 1; arg < argc; arg++) { + if (argv[arg][0] == '-') { + if (argv[arg][1] == 't') { + arg++; + if (arg == argc) { + fprintf( + stderr, + "%s: -t needs a type of file-system;; see -L.\n", + argv[0] + ); + return 1; + } + type = argv[arg]; + } else if (argv[arg][1] == 'r') { + options = RTEMS_FILESYSTEM_READ_ONLY; + } else if (argv[arg][1] == 'L') { + printf ("File systems: "); + rtems_filesystem_iterate(print_filesystem, NULL); + printf ("\n"); + return 0; + } else if (argv[arg][1] == 'o') { + arg++; + if (arg == argc) { + fprintf( + stderr, + "%s: -o needs a list if filesystem options.\n", + argv[0] + ); + return 1; + } + fsoptions = argv[arg]; + } else { + fprintf (stderr, "unknown option: %s\n", argv[arg]); + return 1; + } + } else { + if (!source) + source = argv[arg]; + else if (!target) + target = argv[arg]; + else { + fprintf ( + stderr, "mount: source and mount only require: %s\n", argv[arg]); + return 1; + } + } + } + + if (!type) { + fprintf (stderr, "mount: no file-system; see the -L option\n"); + return 1; + } + + if (!source) { + fprintf (stderr, "mount: no source\n"); + return 1; + } + + if (!target) { + fprintf (stderr, "mount: no mount point\n"); + return 1; + } + + /* + * Mount the disk. + */ + + if (mount (source, target, type, options, fsoptions) < 0) { + fprintf (stderr, "error: %s\n", strerror(errno)); + return 1; + } + + printf ("mounted %s -> %s\n", source, target); + + return 0; +} + +rtems_shell_cmd_t rtems_shell_MOUNT_Command = { + "mount", /* name */ + "mount [-t type] [-r] [-L] source target", /* usage */ + "files", /* topic */ + rtems_shell_main_mount, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_mount_nfs.c b/cpukit/libmisc/shell/main_mount_nfs.c new file mode 100644 index 0000000000..121d0ca6c2 --- /dev/null +++ b/cpukit/libmisc/shell/main_mount_nfs.c @@ -0,0 +1,70 @@ +/* + * Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +#include <librtemsNfs.h> + +static int +rtems_shell_nfs_mounter ( + const char* device, + const char* mntpoint, + rtems_shell_filesystems_t* fs __attribute__((unused)), + rtems_filesystem_options_t options __attribute__((unused))) +{ + char* uidhost; + char* path; + int ret; + + if (strchr (device, ':') == NULL) { + fprintf (stderr, "error: nfs mount device is [uid.gid@]host:path\n"); + return -1; + } + + if (rpcUdpInit () < 0) { + fprintf (stderr, "error: initialising RPC\n"); + return -1; + } + + nfsInit (0, 0); + + uidhost = strdup (device); + path = strchr (uidhost, ':'); + *path = '\0'; + path++; + + ret = nfsMount(uidhost, path, (char*) mntpoint); + + free (uidhost); + + return ret; +} + +rtems_shell_filesystems_t rtems_shell_Mount_NFS = { + name: "nfs", + driver_needed: 1, + fs_ops: NULL, + mounter: rtems_shell_nfs_mounter +}; diff --git a/cpukit/libmisc/shell/main_msdosfmt.c b/cpukit/libmisc/shell/main_msdosfmt.c new file mode 100644 index 0000000000..c682e879eb --- /dev/null +++ b/cpukit/libmisc/shell/main_msdosfmt.c @@ -0,0 +1,185 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <inttypes.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/stringto.h> +#include <rtems/shellconfig.h> +#include <rtems/dosfs.h> +#include <rtems/fsmount.h> +#include "internal.h" + +int rtems_shell_main_msdos_format( + int argc, + char *argv[] +) +{ + msdos_format_request_param_t rqdata = { + OEMName: "RTEMS", + VolLabel: "RTEMSDisk", + sectors_per_cluster: 0, + fat_num: 0, + files_per_root_dir: 0, + fattype: MSDOS_FMT_FATANY, + media: 0, + quick_format: TRUE, + cluster_align: 0, + info_level: 0 + }; + + unsigned long tmp; + const char* driver = NULL; + int arg; + + for (arg = 1; arg < argc; arg++) { + if (argv[arg][0] == '-') { + switch (argv[arg][1]) { + case 'V': + arg++; + if (arg == argc) { + fprintf (stderr, "error: no volume label.\n"); + return 1; + } + rqdata.VolLabel = argv[arg]; + break; + + case 's': + arg++; + if (arg == argc) { + fprintf (stderr, "error: sectors per cluster count.\n"); + return 1; + } + + if ( rtems_string_to_unsigned_long(argv[arg], &tmp, NULL, 0) ) { + printf( + "sector per cluster argument (%s) is not a number\n", + argv[arg] + ); + return -1; + } + + rqdata.sectors_per_cluster = (uint32_t) tmp; + break; + + case 'r': + arg++; + if (arg == argc) { + fprintf (stderr, "error: no root directory size.\n"); + return 1; + } + + if ( rtems_string_to_unsigned_long(argv[arg], &tmp, NULL, 0) ) { + printf( + "root directory size argument (%s) is not a number\n", + argv[arg] + ); + return -1; + } + + rqdata.files_per_root_dir = (uint32_t) tmp; + break; + + case 't': + arg++; + if (arg == argc) { + fprintf (stderr, "error: no FAT type.\n"); + return 1; + } + + if (strcmp (argv[arg], "any") == 0) + rqdata.fattype = MSDOS_FMT_FATANY; + else if (strcmp (argv[arg], "12") == 0) + rqdata.fattype = MSDOS_FMT_FAT12; + else if (strcmp (argv[arg], "16") == 0) + rqdata.fattype = MSDOS_FMT_FAT16; + else if (strcmp (argv[arg], "32") == 0) + rqdata.fattype = MSDOS_FMT_FAT32; + else { + fprintf (stderr, "error: invalid type, can any, 12, 16, or 32\n"); + return 1; + } + break; + + case 'v': + rqdata.info_level++; + break; + + default: + fprintf (stderr, "error: invalid option: %s\n", argv[arg]); + return 1; + + } + } else { + if (!driver) + driver = argv[arg]; + else { + fprintf (stderr, "error: only one driver allowed: %s\n", argv[arg]); + return 1; + } + } + } + + if (!driver) { + fprintf (stderr, "error: no driver\n"); + return 1; + } + + printf ("msdos format: %s\n", driver); + + if (rqdata.info_level) + { + printf (" %-20s: %s\n", "OEMName", "RTEMS"); + printf (" %-20s: %s\n", "VolLabel", "RTEMSDisk"); + printf (" %-20s: %" PRIu32 "\n", "sectors per cluster", rqdata.sectors_per_cluster); + printf (" %-20s: %" PRIu32 "\n", "fats", rqdata.fat_num); + printf (" %-20s: %" PRIu32 "\n", "files per root dir", rqdata.files_per_root_dir); + printf (" %-20s: %i\n", "fat type", rqdata.fattype); + printf (" %-20s: %d\n", "media", rqdata.media); + printf (" %-20s: %d\n", "quick_format", rqdata.quick_format); + printf (" %-20s: %" PRIu32 "\n", "cluster align", rqdata.cluster_align); + } + + if (msdos_format (driver, &rqdata) < 0) { + fprintf (stderr, "error: format failed: %s\n", strerror (errno)); + return 1; + } + + printf ("msdos format successful\n"); + + return 0; +} + +#define OPTIONS "[-v label] [-r size] [-t any/12/16/32]" + +rtems_shell_cmd_t rtems_shell_MSDOSFMT_Command = { + "mkdos", /* name */ + "mkdos " OPTIONS " path # format disk", /* usage */ + "files", /* topic */ + rtems_shell_main_msdos_format, /* command */ + NULL, /* alias */ + NULL /* next */ +}; + +rtems_shell_cmd_t rtems_shell_MSDOSFMT_Alias = { + "msdosfmt", /* name */ + NULL, /* usage */ + "files", /* topic */ + NULL, /* command */ + &rtems_shell_MSDOSFMT_Command, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_mv.c b/cpukit/libmisc/shell/main_mv.c new file mode 100644 index 0000000000..644900bc17 --- /dev/null +++ b/cpukit/libmisc/shell/main_mv.c @@ -0,0 +1,491 @@ +/* $NetBSD: mv.c,v 1.41 2008/07/20 00:52:40 lukem Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ken Smith of The State University of New York at Buffalo. + * + * 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if 0 +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mv.c 8.2 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: mv.c,v 1.41 2008/07/20 00:52:40 lukem Exp $"); +#endif +#endif /* not lint */ +#endif + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/shellconfig.h> +#define __need_getopt_newlib +#include <getopt.h> + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <grp.h> +#include <locale.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pathnames-mv.h" + +/* RTEMS specific changes */ +typedef struct { + int fflg, iflg, vflg; + int stdin_ok; + int exit_code; + jmp_buf exit_jmp; +} rtems_shell_mv_globals; + +int copy(char *, char *); +int do_move(char *, char *); +int fastcopy(char *, char *, struct stat *); +void usage(void); +int main(int, char *[]); + +#define fflg globals->fflg +#define iflg globals->iflg +#define vflg globals->vflg +#define stdin_ok globals->stdin_ok +#define exit_jump &(globals->exit_jmp) + +#include <setjmp.h> + +#define exit(ec) rtems_shell_mv_exit(globals, ec) + +void +rtems_shell_mv_exit (rtems_shell_mv_globals* globals, int code) +{ + globals->exit_code = code; + longjmp (globals->exit_jmp, 1); +} + +void strmode(mode_t mode, char *p); +const char *user_from_uid(uid_t uid, int nouser); +char *group_from_gid(gid_t gid, int nogroup); + +static int main_mv(rtems_shell_mv_globals* globals, int argc, char *argv[]); + +int rtems_shell_main_cp(int argc, char *argv[]); +int rtems_shell_main_rm(int argc, char *argv[]); + +int +rtems_shell_main_mv(int argc, char *argv[]) +{ + rtems_shell_mv_globals mv_globals; + memset (&mv_globals, 0, sizeof (mv_globals)); + mv_globals.exit_code = 1; + if (setjmp (mv_globals.exit_jmp) == 0) + return main_mv (&mv_globals, argc, argv); + return mv_globals.exit_code; +} + +#define do_move(a1, a2) do_move_mv(globals, a1, a2) +#define fastcopy(a1, a2, a3) fastcopy_mv(globals, a1, a2, a3) +#define copy(a1, a2) copy_mv(globals, a1, a2) +#define usage() usage_mv(globals) + +static int do_move_mv(rtems_shell_mv_globals* globals, char *from, char *to); +static int fastcopy_mv(rtems_shell_mv_globals* globals, + char *from, char *to, struct stat *sbp); +static int copy_mv(rtems_shell_mv_globals* globals, char *from, char *to); +static void usage_mv(rtems_shell_mv_globals* globals); + +/* RTEMS changes */ + +int +main_mv(rtems_shell_mv_globals* globals, int argc, char *argv[]) +{ + int ch, len, rval; + char *p, *endp; + struct stat sb; + char path[MAXPATHLEN + 1]; + size_t baselen; + + struct getopt_data getopt_reent; + memset(&getopt_reent, 0, sizeof(getopt_data)); + +/* setprogname(argv[0]); */ + + (void)setlocale(LC_ALL, ""); + + while ((ch = getopt_r(argc, argv, "ifv", &getopt_reent)) != -1) + switch (ch) { + case 'i': + fflg = 0; + iflg = 1; + break; + case 'f': + iflg = 0; + fflg = 1; + break; + case 'v': + vflg = 1; + break; + default: + usage(); + } + argc -= getopt_reent.optind; + argv += getopt_reent.optind; + + if (argc < 2) + usage(); + + stdin_ok = isatty(STDIN_FILENO); + + /* + * If the stat on the target fails or the target isn't a directory, + * try the move. More than 2 arguments is an error in this case. + */ + if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { + if (argc > 2) + usage(); + exit(do_move(argv[0], argv[1])); + } + + /* It's a directory, move each file into it. */ + baselen = strlcpy(path, argv[argc - 1], sizeof(path)); + if (baselen >= sizeof(path)) + errx(exit_jump, 1, "%s: destination pathname too long", argv[argc - 1]); + endp = &path[baselen]; + if (!baselen || *(endp - 1) != '/') { + *endp++ = '/'; + ++baselen; + } + for (rval = 0; --argc; ++argv) { + p = *argv + strlen(*argv) - 1; + while (*p == '/' && p != *argv) + *p-- = '\0'; + if ((p = strrchr(*argv, '/')) == NULL) + p = *argv; + else + ++p; + + if ((baselen + (len = strlen(p))) >= MAXPATHLEN) { + warnx("%s: destination pathname too long", *argv); + rval = 1; + } else { + memmove(endp, p, len + 1); + if (do_move(*argv, path)) + rval = 1; + } + } + return rval; +} + +int +do_move_mv(rtems_shell_mv_globals* globals, char *from, char *to) +{ + struct stat sb; + char modep[15]; + + /* + * (1) If the destination path exists, the -f option is not specified + * and either of the following conditions are true: + * + * (a) The permissions of the destination path do not permit + * writing and the standard input is a terminal. + * (b) The -i option is specified. + * + * the mv utility shall write a prompt to standard error and + * read a line from standard input. If the response is not + * affirmative, mv shall do nothing more with the current + * source file... + */ + if (!fflg && !access(to, F_OK)) { + int ask = 1; + int ch; + + if (iflg) { + if (access(from, F_OK)) { + warn("rename %s", from); + return (1); + } + (void)fprintf(stderr, "overwrite %s? ", to); + } else if (stdin_ok && access(to, W_OK) && !stat(to, &sb)) { + if (access(from, F_OK)) { + warn("rename %s", from); + return (1); + } + strmode(sb.st_mode, modep); + (void)fprintf(stderr, "override %s%s%s/%s for %s? ", + modep + 1, modep[9] == ' ' ? "" : " ", + user_from_uid(sb.st_uid, 0), + group_from_gid(sb.st_gid, 0), to); + } else + ask = 0; + if (ask) { + if ((ch = getchar()) != EOF && ch != '\n') { + int ch2; + while ((ch2 = getchar()) != EOF && ch2 != '\n') + continue; + } + if (ch != 'y' && ch != 'Y') + return (0); + } + } + + /* + * (2) If rename() succeeds, mv shall do nothing more with the + * current source file. If it fails for any other reason than + * EXDEV, mv shall write a diagnostic message to the standard + * error and do nothing more with the current source file. + * + * (3) If the destination path exists, and it is a file of type + * directory and source_file is not a file of type directory, + * or it is a file not of type directory, and source file is + * a file of type directory, mv shall write a diagnostic + * message to standard error, and do nothing more with the + * current source file... + */ + if (!rename(from, to)) { + if (vflg) + printf("%s -> %s\n", from, to); + return (0); + } + + if (errno != EXDEV) { + warn("rename %s to %s", from, to); + return (1); + } + + /* + * (4) If the destination path exists, mv shall attempt to remove it. + * If this fails for any reason, mv shall write a diagnostic + * message to the standard error and do nothing more with the + * current source file... + */ + if (!lstat(to, &sb)) { + if ((S_ISDIR(sb.st_mode)) ? rmdir(to) : unlink(to)) { + warn("can't remove %s", to); + return (1); + } + } + + /* + * (5) The file hierarchy rooted in source_file shall be duplicated + * as a file hierarchy rooted in the destination path... + */ + if (lstat(from, &sb)) { + warn("%s", from); + return (1); + } + + return (S_ISREG(sb.st_mode) ? + fastcopy(from, to, &sb) : copy(from, to)); +} + +int +fastcopy_mv(rtems_shell_mv_globals* globals, char *from, char *to, struct stat *sbp) +{ + struct timeval tval[2]; + uint32_t blen; + static char *bp; + int nread, from_fd, to_fd; + + blen = 0; + + if ((from_fd = open(from, O_RDONLY, 0)) < 0) { + warn("%s", from); + return (1); + } + if ((to_fd = + open(to, O_CREAT | O_TRUNC | O_WRONLY, sbp->st_mode)) < 0) { + warn("%s", to); + (void)close(from_fd); + return (1); + } + if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { + warn(NULL); + blen = 0; + (void)close(from_fd); + (void)close(to_fd); + return (1); + } + while ((nread = read(from_fd, bp, blen)) > 0) + if (write(to_fd, bp, nread) != nread) { + warn("%s", to); + goto err; + } + if (nread < 0) { + warn("%s", from); +err: if (unlink(to)) + warn("%s: remove", to); + (void)free(bp); + (void)close(from_fd); + (void)close(to_fd); + return (1); + } + + (void)free(bp); + (void)close(from_fd); +#ifdef xBSD4_4 + TIMESPEC_TO_TIMEVAL(&tval[0], &sbp->st_atimespec); + TIMESPEC_TO_TIMEVAL(&tval[1], &sbp->st_mtimespec); +#else + tval[0].tv_sec = sbp->st_atime; + tval[1].tv_sec = sbp->st_mtime; + tval[0].tv_usec = 0; + tval[1].tv_usec = 0; +#endif +#if 0 +#ifdef _SRV5 + if (utimes(to, tval)) +#else + if (futimes(to_fd, tval)) +#endif + warn("%s: set times", to); +#endif + if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) { + if (errno != EPERM) + warn("%s: set owner/group", to); + sbp->st_mode &= ~(S_ISUID | S_ISGID); + } + if (fchmod(to_fd, sbp->st_mode)) + warn("%s: set mode", to); +#if 0 + if (fchflags(to_fd, sbp->st_flags) && (errno != EOPNOTSUPP)) + warn("%s: set flags (was: 0%07o)", to, sbp->st_flags); +#endif + if (close(to_fd)) { + warn("%s", to); + return (1); + } + + if (unlink(from)) { + warn("%s: remove", from); + return (1); + } + + if (vflg) + printf("%s -> %s\n", from, to); + + return (0); +} + +int +copy_mv(rtems_shell_mv_globals* globals, char *from, char *to) +{ + char* cp_argv[5] = { "mv", vflg ? "-PRpv" : "-PRp", "--", from, to }; + char* rm_argv[4] = { "mv", "-rf", "--", from }; + int result; + + result = rtems_shell_main_cp(5, cp_argv); + if (result) { + warnx("%s: did not terminate normally", _PATH_CP); + return (1); + } + result = rtems_shell_main_rm(4, rm_argv); + if (result) { + warnx("%s: did not terminate normally", _PATH_RM); + return (1); + } +#if 0 + pid_t pid; + int status; + + if ((pid = vfork()) == 0) { + execl(_PATH_CP, "mv", vflg ? "-PRpv" : "-PRp", "--", from, to, NULL); + warn("%s", _PATH_CP); + _exit(1); + } + if (waitpid(pid, &status, 0) == -1) { + warn("%s: waitpid", _PATH_CP); + return (1); + } + if (!WIFEXITED(status)) { + warnx("%s: did not terminate normally", _PATH_CP); + return (1); + } + if (WEXITSTATUS(status)) { + warnx("%s: terminated with %d (non-zero) status", + _PATH_CP, WEXITSTATUS(status)); + return (1); + } + if (!(pid = vfork())) { + execl(_PATH_RM, "mv", "-rf", "--", from, NULL); + warn("%s", _PATH_RM); + _exit(1); + } + if (waitpid(pid, &status, 0) == -1) { + warn("%s: waitpid", _PATH_RM); + return (1); + } + if (!WIFEXITED(status)) { + warnx("%s: did not terminate normally", _PATH_RM); + return (1); + } + if (WEXITSTATUS(status)) { + warnx("%s: terminated with %d (non-zero) status", + _PATH_RM, WEXITSTATUS(status)); + return (1); + } +#endif + return (0); +} + +void +usage_mv(rtems_shell_mv_globals* globals) +{ + (void)fprintf(stderr, "usage: %s [-fiv] source target\n" + " %s [-fiv] source ... directory\n", + "mv", "mv"); + exit(1); + /* NOTREACHED */ +} + +rtems_shell_cmd_t rtems_shell_MV_Command = { + "mv", /* name */ + "[-fiv] source target ...", /* usage */ + "files", /* topic */ + rtems_shell_main_mv, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_mwdump.c b/cpukit/libmisc/shell/main_mwdump.c new file mode 100644 index 0000000000..f3e3cfc27b --- /dev/null +++ b/cpukit/libmisc/shell/main_mwdump.c @@ -0,0 +1,95 @@ +/* + * MWDUMP Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/stringto.h> +#include "internal.h" + +int rtems_shell_main_mwdump( + int argc, + char *argv[] +) +{ + unsigned char n; + unsigned char m; + int max; + int res; + void *addr = 0; + unsigned char *pb; + + if ( argc > 1 ) { + if ( rtems_string_to_pointer(argv[1], &addr, NULL) ) { + printf( "Address argument (%s) is not a number\n", argv[1] ); + return -1; + } + } + + if ( argc > 2 ) { + if ( rtems_string_to_int(argv[2], &max, NULL, 0) ) { + printf( "Address argument (%s) is not a number\n", argv[1] ); + return -1; + } + + if (max <= 0) { + max = 1; /* print 1 item if 0 or neg. */ + res = 0; + } else { + max--; + res = max & 0xf;/* num bytes in last row */ + max >>= 4; /* div by 16 */ + max++; /* num of rows to print */ + if (max > 20) { /* limit to 20 */ + max = 20; + res = 0xf; /* 16 bytes print in last row */ + } + } + } else { + max = 20; + res = 0xf; + } + + pb = addr; + for (m=0;m<max;m++) { + printf("%10p ", pb); + for (n=0;n<=(m==(max-1)?res:0xf);n+=2) + printf("%04X%c",*((unsigned short*)(pb+n)),n==6?'-':' '); + for (;n<=0xf;n+=2) + printf(" %c", n==6?'-':' '); + for (n=0;n<=(m==(max-1)?res:0xf);n++) { + printf("%c", isprint(pb[n]) ? pb[n] : '.'); + } + printf("\n"); + pb += 16; + } + return 0; +} + +rtems_shell_cmd_t rtems_shell_WDUMP_Command = { + "wdump", /* name */ + "wdump [address [length]]", /* usage */ + "mem", /* topic */ + rtems_shell_main_mwdump, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_netstats.c b/cpukit/libmisc/shell/main_netstats.c new file mode 100644 index 0000000000..cc9b95a52c --- /dev/null +++ b/cpukit/libmisc/shell/main_netstats.c @@ -0,0 +1,138 @@ +/* + * Network Statistics Shell Command Implmentation + * + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#define __need_getopt_newlib +#include <getopt.h> + +#include <rtems.h> +#include <rtems/rtems_bsdnet.h> +#include <rtems/shell.h> +#include "internal.h" + +static void netstats_usage(void) +{ + printf( + "netstats [-vAimfpcut] where:\n" + " -A print All statistics\n" + " -i print Inet Routes\n" + " -m print MBUF Statistics\n" + " -f print IF Statistics\n" + " -p print IP Statistics\n" + " -c print ICMP Statistics\n" + " -u print UDP Statistics\n" + " -t print TCP Statistics\n" + ); +} + +int rtems_shell_main_netstats( /* command */ + int argc, + char *argv[] +) +{ + int option; + int doAll = 0; + int doInetRoutes = 0; + int doMBUFStats = 0; + int doIFStats = 0; + int doIPStats = 0; + int doICMPStats = 0; + int doUDPStats = 0; + int doTCPStats = 0; + int verbose = 0; + struct getopt_data getopt_reent; + + memset(&getopt_reent, 0, sizeof(getopt_data)); + while ( (option = getopt_r( argc, argv, "Aimfpcutv", &getopt_reent)) != -1 ) { + + switch ((char)option) { + case 'A': doAll = 1; break; + case 'i': doInetRoutes = 1; break; + case 'm': doMBUFStats = 1; break; + case 'f': doIFStats = 1; break; + case 'p': doIPStats = 1; break; + case 'c': doICMPStats = 1; break; + case 'u': doUDPStats = 1; break; + case 't': doTCPStats = 1; break; + case 'v': verbose = 1; break; + case '?': + default: + netstats_usage(); + return -1; + } + } + + if ( verbose ) { + printf( + "doAll=%d\n" + "doInetRoutes=%d\n" + "doMBUFStats=%d\n" + "doIFStats=%d\n" + "doIPStats=%d\n" + "doICMPStats=%d\n" + "doUDPStats=%d\n" + "doTCPStats=%d\n", + doAll, + doInetRoutes, + doMBUFStats, + doIFStats, + doIPStats, + doICMPStats, + doUDPStats, + doTCPStats + ); + } + + if ( doInetRoutes == 1 || doAll == 1 ) { + rtems_bsdnet_show_inet_routes(); + } + + if ( doMBUFStats == 1 || doAll == 1 ) { + rtems_bsdnet_show_mbuf_stats(); + } + + if ( doIFStats == 1 || doAll == 1 ) { + rtems_bsdnet_show_if_stats(); + } + + if ( doIPStats == 1 || doAll == 1 ) { + rtems_bsdnet_show_ip_stats(); + } + + if ( doICMPStats == 1 || doAll == 1 ) { + rtems_bsdnet_show_icmp_stats(); + } + + if ( doUDPStats == 1 || doAll == 1 ) { + rtems_bsdnet_show_udp_stats(); + } + + if ( doTCPStats == 1 || doAll == 1 ) { + rtems_bsdnet_show_tcp_stats(); + } + + return 0; +} + +rtems_shell_cmd_t rtems_shell_NETSTATS_Command = { + "netstats", /* name */ + "netstats [-Aimfpcutv]", /* usage */ + "network", /* topic */ + rtems_shell_main_netstats, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_perioduse.c b/cpukit/libmisc/shell/main_perioduse.c new file mode 100644 index 0000000000..d0f123af1c --- /dev/null +++ b/cpukit/libmisc/shell/main_perioduse.c @@ -0,0 +1,63 @@ +/* + * perioduse Command Implementation + * + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_perioduse( + int argc, + char *argv[] +) +{ + /* + * When invoked with no arguments, print the report. + */ + if ( argc == 1 ) { + rtems_rate_monotonic_report_statistics_with_plugin( + stdout, + (rtems_printk_plugin_t)fprintf + ); + return 0; + } + + /* + * When invoked with the single argument -r, reset the statistics. + */ + if ( argc == 2 && !strcmp( argv[1], "-r" ) ) { + printf( "Resetting Period Usage information\n" ); + rtems_rate_monotonic_reset_all_statistics(); + return 0; + } + + /* + * OK. The user did something wrong. + */ + fprintf( stderr, "%s: [-r]\n", argv[0] ); + return -1; +} + +rtems_shell_cmd_t rtems_shell_PERIODUSE_Command = { + "perioduse", /* name */ + "[-r] print or reset per period usage", /* usage */ + "rtems", /* topic */ + rtems_shell_main_perioduse, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_pwd.c b/cpukit/libmisc/shell/main_pwd.c new file mode 100644 index 0000000000..3a298f8242 --- /dev/null +++ b/cpukit/libmisc/shell/main_pwd.c @@ -0,0 +1,45 @@ +/* + * PWD Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_pwd( + int argc __attribute__((unused)), + char *argv[] __attribute__((unused)) +) +{ + char dir[1024]; + + getcwd(dir,1024); + puts(dir); + return 0; +} + +rtems_shell_cmd_t rtems_shell_PWD_Command = { + "pwd", /* name */ + "pwd # print work directory", /* usage */ + "files", /* topic */ + rtems_shell_main_pwd, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_rm.c b/cpukit/libmisc/shell/main_rm.c new file mode 100644 index 0000000000..465a0daf46 --- /dev/null +++ b/cpukit/libmisc/shell/main_rm.c @@ -0,0 +1,709 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. 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. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if 0 +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94"; +#endif /* not lint */ +#endif +#if 0 +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/bin/rm/rm.c,v 1.58 2006/10/31 02:22:36 delphij Exp $"); +#endif + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/shellconfig.h> +#define __need_getopt_newlib +#include <getopt.h> + +#include <sys/stat.h> +#include <sys/param.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <grp.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define rindex(s,c) strrchr(s,c) + +/* RTEMS specific changes */ +typedef struct { + int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok; + int rflag, Iflag; + uid_t uid; + int exit_code; + jmp_buf exit_jmp; +} rtems_shell_rm_globals; + +#define dflag globals->dflag +#define eval globals->eval +#define fflag globals->fflag +#define iflag globals->iflag +#define Pflag globals->Pflag +#define vflag globals->vflag +#define Wflag globals->Wflag +#define stdin_ok globals->stdin_ok +#define rflag globals->rflag +#define Iflag globals->Iflag +#define xuid globals->uid +#define exit_jump &(globals->exit_jmp) + +#include <setjmp.h> + +#define exit(ec) rtems_shell_rm_exit(globals, ec) +void +rtems_shell_rm_exit (rtems_shell_rm_globals* globals, int code) +{ + globals->exit_code = code; + longjmp (globals->exit_jmp, 1); +} + +static int main_rm(rtems_shell_rm_globals* globals, int argc, char *argv[]); + +int +rtems_shell_main_rm(int argc, char *argv[]) +{ + rtems_shell_rm_globals rm_globals; + memset (&rm_globals, 0, sizeof (rm_globals)); + rm_globals.exit_code = 1; + if (setjmp (rm_globals.exit_jmp) == 0) + return main_rm (&rm_globals, argc, argv); + return rm_globals.exit_code; +} + +#define check(a1, a2, a3) check_rm(globals, a1, a2, a3) +#define check2(a1) check2_rm(globals, a1) +#define checkdot(a1) checkdot_rm(globals, a1) +#define checkslash(a1) checkslash_rm(globals, a1) +#define rm_file(a1) rm_file_rm(globals, a1) +#define rm_overwrite(a1, a2) rm_overwrite_rm(globals, a1, a2) +#define rm_tree(a1) rm_tree_rm(globals, a1) +#define usage() usage_rm(globals) + + +/* RTEMS changes */ + +static int check_rm(rtems_shell_rm_globals* globals, char *, char *, struct stat *); +static int check2_rm(rtems_shell_rm_globals* globals, char **); +static void checkdot_rm(rtems_shell_rm_globals* globals, char **); +static void checkslash_rm(rtems_shell_rm_globals* globals, char **); +static void rm_file_rm(rtems_shell_rm_globals* globals, char **); +static int rm_overwrite_rm(rtems_shell_rm_globals* globals, char *, struct stat *); +static void rm_tree_rm(rtems_shell_rm_globals* globals, char **); +static void usage_rm(rtems_shell_rm_globals* globals); + +/* + * rm -- + * This rm is different from historic rm's, but is expected to match + * POSIX 1003.2 behavior. The most visible difference is that -f + * has two specific effects now, ignore non-existent files and force + * file removal. + */ +int +main_rm(rtems_shell_rm_globals* globals, int argc, char *argv[]) +{ + int ch; + char *p; + + struct getopt_data getopt_reent; + memset(&getopt_reent, 0, sizeof(getopt_data)); + + /* + * Test for the special case where the utility is called as + * "unlink", for which the functionality provided is greatly + * simplified. + */ + if ((p = rindex(argv[0], '/')) == NULL) + p = argv[0]; + else + ++p; + if (strcmp(p, "unlink") == 0) { + while (getopt_r(argc, argv, "", &getopt_reent) != -1) + usage(); + argc -= getopt_reent.optind; + argv += getopt_reent.optind; + if (argc != 1) + usage(); + rm_file(&argv[0]); + exit(eval); + } + + Pflag = rflag = 0; + while ((ch = getopt_r(argc, argv, "dfiIPRrvW", &getopt_reent)) != -1) + switch(ch) { + case 'd': + dflag = 1; + break; + case 'f': + fflag = 1; + iflag = 0; + break; + case 'i': + fflag = 0; + iflag = 1; + break; + case 'I': + Iflag = 1; + break; + case 'P': + Pflag = 1; + break; + case 'R': + case 'r': /* Compatibility. */ + rflag = 1; + break; + case 'v': + vflag = 1; + break; + case 'W': + Wflag = 1; + break; + default: + usage(); + } + argc -= getopt_reent.optind; + argv += getopt_reent.optind; + + if (argc < 1) { + if (fflag) + return (0); + usage(); + } + + checkdot(argv); + if (getenv("POSIXLY_CORRECT") == NULL) + checkslash(argv); + xuid = geteuid(); + + if (*argv) { + stdin_ok = isatty(STDIN_FILENO); + + if (Iflag) { + if (check2(argv) == 0) + exit (1); + } + if (rflag) + rm_tree(argv); + else + rm_file(argv); + } + + exit (eval); + return 0; +} + +void +rm_tree_rm(rtems_shell_rm_globals* globals, char **argv) +{ + FTS *fts; + FTSENT *p; + int needstat; + int flags; + int rval; + + /* + * Remove a file hierarchy. If forcing removal (-f), or interactive + * (-i) or can't ask anyway (stdin_ok), don't stat the file. + */ + needstat = !xuid || (!fflag && !iflag && stdin_ok); + + /* + * If the -i option is specified, the user can skip on the pre-order + * visit. The fts_number field flags skipped directories. + */ +#define SKIPPED 1 + + flags = FTS_PHYSICAL; + if (!needstat) + flags |= FTS_NOSTAT; + if (Wflag) + flags |= FTS_WHITEOUT; + if (!(fts = fts_open(argv, flags, NULL))) { + if (fflag && errno == ENOENT) + return; + err(exit_jump, 1, "fts_open"); + } + while ((p = fts_read(fts)) != NULL) { + switch (p->fts_info) { + case FTS_DNR: + if (!fflag || p->fts_errno != ENOENT) { + warnx("%s: %s", + p->fts_path, strerror(p->fts_errno)); + eval = 1; + } + continue; + case FTS_ERR: + errx(exit_jump, 1, "%s: %s", p->fts_path, strerror(p->fts_errno)); + case FTS_NS: + /* + * Assume that since fts_read() couldn't stat the + * file, it can't be unlinked. + */ + if (!needstat) + break; + if (!fflag || p->fts_errno != ENOENT) { + warnx("%s: %s", + p->fts_path, strerror(p->fts_errno)); + eval = 1; + } + continue; + case FTS_D: + /* Pre-order: give user chance to skip. */ + if (!fflag && !check(p->fts_path, p->fts_accpath, + p->fts_statp)) { + (void)fts_set(fts, p, FTS_SKIP); + p->fts_number = SKIPPED; + } +#if RTEMS_REMOVED + else if (!xuid && + (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && + chflags(p->fts_accpath, + p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) + goto err; +#endif + continue; + case FTS_DP: + /* Post-order: see if user skipped. */ + if (p->fts_number == SKIPPED) + continue; + break; + default: + if (!fflag && + !check(p->fts_path, p->fts_accpath, p->fts_statp)) + continue; + } + + rval = 0; +#if RTEMS_REMOVED + if (!xuid && + (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))) + rval = chflags(p->fts_accpath, + p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); +#endif + if (rval == 0) { + /* + * If we can't read or search the directory, may still be + * able to remove it. Don't print out the un{read,search}able + * message unless the remove fails. + */ + switch (p->fts_info) { + case FTS_DP: + case FTS_DNR: + rval = rmdir(p->fts_accpath); + if (rval == 0 || (fflag && errno == ENOENT)) { + if (rval == 0 && vflag) + (void)printf("%s\n", + p->fts_path); + continue; + } + break; + +#if RTEMS_REMOVED + case FTS_W: + rval = undelete(p->fts_accpath); + if (rval == 0 && (fflag && errno == ENOENT)) { + if (vflag) + (void)printf("%s\n", + p->fts_path); + continue; + } + break; +#endif + + case FTS_NS: + /* + * Assume that since fts_read() couldn't stat + * the file, it can't be unlinked. + */ + if (fflag) + continue; + /* FALLTHROUGH */ + default: + if (Pflag) + if (!rm_overwrite(p->fts_accpath, NULL)) + continue; + rval = unlink(p->fts_accpath); + if (rval == 0 || (fflag && errno == ENOENT)) { + if (rval == 0 && vflag) + (void)printf("%s\n", + p->fts_path); + continue; + } + } + } +#if RTEMS_REMOVED +err: +#endif + warn("%s", p->fts_path); + eval = 1; + } + if (errno) + err(exit_jump, 1, "fts_read"); + fts_close(fts); +} + +#define S_ISWHT(m) (0) + +void +rm_file_rm(rtems_shell_rm_globals* globals, char **argv) +{ + struct stat sb; + int rval; + char *f; + + /* + * Remove a file. POSIX 1003.2 states that, by default, attempting + * to remove a directory is an error, so must always stat the file. + */ + while ((f = *argv++) != NULL) { + /* Assume if can't stat the file, can't unlink it. */ + if (lstat(f, &sb)) { +#if RTEMS_REMOVED + if (Wflag) { + sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR; + } else { +#endif + if (!fflag || errno != ENOENT) { + warn("%s", f); + eval = 1; + } + continue; +#if RTEMS_REMOVED + } +#endif + } else if (Wflag) { + warnx("%s: %s", f, strerror(EEXIST)); + eval = 1; + continue; + } + + if (S_ISDIR(sb.st_mode) && !dflag) { + warnx("%s: is a directory", f); + eval = 1; + continue; + } + if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb)) + continue; + rval = 0; +#if RTEMS_REMOVED + if (!xuid && !S_ISWHT(sb.st_mode) && + (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE))) + rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE)); +#endif + if (rval == 0) { + if (S_ISWHT(sb.st_mode)) +#if RTEMS_REMOVED + rval = undelete(f); +#endif + ; + else if (S_ISDIR(sb.st_mode)) + rval = rmdir(f); + else { + if (Pflag) + if (!rm_overwrite(f, &sb)) + continue; + rval = unlink(f); + } + } + if (rval && (!fflag || errno != ENOENT)) { + warn("%s", f); + eval = 1; + } + if (vflag && rval == 0) + (void)printf("%s\n", f); + } +} + +/* + * rm_overwrite -- + * Overwrite the file 3 times with varying bit patterns. + * + * XXX + * This is a cheap way to *really* delete files. Note that only regular + * files are deleted, directories (and therefore names) will remain. + * Also, this assumes a fixed-block file system (like FFS, or a V7 or a + * System V file system). In a logging file system, you'll have to have + * kernel support. + */ +int +rm_overwrite_rm(rtems_shell_rm_globals* globals, char *file, struct stat *sbp) +{ + struct stat sb; +#if RTEMS_REMOVED + struct statfs fsb; +#endif + off_t len; + int bsize, fd, wlen; + char *buf = NULL; + + fd = -1; + if (sbp == NULL) { + if (lstat(file, &sb)) + goto err; + sbp = &sb; + } + if (!S_ISREG(sbp->st_mode)) + return (1); + if (sbp->st_nlink > 1 && !fflag) { + warnx("%s (inode %lu): not overwritten due to multiple links", + file, sbp->st_ino); + return (0); + } + if ((fd = open(file, O_WRONLY, 0)) == -1) + goto err; +#if RTEMS_REMOVED + if (fstatfs(fd, &fsb) == -1) + goto err; + bsize = MAX(fsb.f_iosize, 1024); +#endif + bsize = 1024; + if ((buf = malloc(bsize)) == NULL) + err(exit_jump, 1, "%s: malloc", file); + +#define PASS(byte) { \ + memset(buf, byte, bsize); \ + for (len = sbp->st_size; len > 0; len -= wlen) { \ + wlen = len < bsize ? len : bsize; \ + if (write(fd, buf, wlen) != wlen) \ + goto err; \ + } \ +} + PASS(0xff); + if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) + goto err; + PASS(0x00); + if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) + goto err; + PASS(0xff); + if (!fsync(fd) && !close(fd)) { + free(buf); + return (1); + } + +err: eval = 1; + if (buf) + free(buf); + if (fd != -1) + close(fd); + warn("%s", file); + return (0); +} + +void strmode(mode_t mode, char *p); +char *fflagstostr(u_long flags); +const char *user_from_uid(uid_t uid, int nouser); + +int +check_rm(rtems_shell_rm_globals* globals, char *path, char *name, struct stat *sp) +{ + int ch, first; + char modep[15], *flagsp; + + /* Check -i first. */ + if (iflag) + (void)fprintf(stderr, "remove %s? ", path); + else { + /* + * If it's not a symbolic link and it's unwritable and we're + * talking to a terminal, ask. Symbolic links are excluded + * because their permissions are meaningless. Check stdin_ok + * first because we may not have stat'ed the file. + */ +#if RTEMS_REMOVED + if (!stdin_ok || S_ISLNK(sp->st_mode) || + (!access(name, W_OK) && + !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && + (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !xuid))) +#endif + if (!stdin_ok || S_ISLNK(sp->st_mode) || + (!access(name, W_OK))) + return (1); + strmode(sp->st_mode, modep); +#if RTEMS_REMOVED + if ((flagsp = fflagstostr(sp->st_flags)) == NULL) + err(exit_jump, 1, "fflagstostr"); +#else + flagsp = "no supported"; +#endif + if (Pflag) + errx(exit_jump, 1, + "%s: -P was specified, but file is not writable", + path); + (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ", + modep + 1, modep[9] == ' ' ? "" : " ", + user_from_uid(sp->st_uid, 0), + group_from_gid(sp->st_gid, 0), + *flagsp ? flagsp : "", *flagsp ? " " : "", + path); +#if RTEMS_REMOVED + free(flagsp); +#endif + } + (void)fflush(stderr); + + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + return (first == 'y' || first == 'Y'); +} + +#define ISSLASH(a) ((a)[0] == '/' && (a)[1] == '\0') +void +checkslash_rm(rtems_shell_rm_globals* globals, char **argv) +{ + char **t, **u; + int complained; + + complained = 0; + for (t = argv; *t;) { + if (ISSLASH(*t)) { + if (!complained++) + warnx("\"/\" may not be removed"); + eval = 1; + for (u = t; u[0] != NULL; ++u) + u[0] = u[1]; + } else { + ++t; + } + } +} + +int +check2_rm(rtems_shell_rm_globals* globals, char **argv) +{ + struct stat st; + int first; + int ch; + int fcount = 0; + int dcount = 0; + int i; + const char *dname = NULL; + + for (i = 0; argv[i]; ++i) { + if (lstat(argv[i], &st) == 0) { + if (S_ISDIR(st.st_mode)) { + ++dcount; + dname = argv[i]; /* only used if 1 dir */ + } else { + ++fcount; + } + } + } + first = 0; + while (first != 'n' && first != 'N' && first != 'y' && first != 'Y') { + if (dcount && rflag) { + fprintf(stderr, "recursively remove"); + if (dcount == 1) + fprintf(stderr, " %s", dname); + else + fprintf(stderr, " %d dirs", dcount); + if (fcount == 1) + fprintf(stderr, " and 1 file"); + else if (fcount > 1) + fprintf(stderr, " and %d files", fcount); + } else if (dcount + fcount > 3) { + fprintf(stderr, "remove %d files", dcount + fcount); + } else { + return(1); + } + fprintf(stderr, "? "); + fflush(stderr); + + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (ch == EOF) + break; + } + return (first == 'y' || first == 'Y'); +} + +#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) +void +checkdot_rm(rtems_shell_rm_globals* globals, char **argv) +{ + char *p, **save, **t; + int complained; + + complained = 0; + for (t = argv; *t;) { + if ((p = strrchr(*t, '/')) != NULL) + ++p; + else + p = *t; + if (ISDOT(p)) { + if (!complained++) + warnx("\".\" and \"..\" may not be removed"); + eval = 1; + for (save = t; (t[0] = t[1]) != NULL; ++t) + continue; + t = save; + } else + ++t; + } +} + +void +usage_rm(rtems_shell_rm_globals* globals) +{ + + (void)fprintf(stderr, "%s\n%s\n", + "usage: rm [-f | -i] [-dIPRrvW] file ...", + " unlink file"); + exit(1); +} + +rtems_shell_cmd_t rtems_shell_RM_Command = { + "rm", /* name */ + "[-f | -i] [-dIPRrvW] file ...", /* usage */ + "files", /* topic */ + rtems_shell_main_rm, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_rmdir.c b/cpukit/libmisc/shell/main_rmdir.c new file mode 100644 index 0000000000..df94a7efbf --- /dev/null +++ b/cpukit/libmisc/shell/main_rmdir.c @@ -0,0 +1,51 @@ +/* + * RMDIR Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_rmdir (int argc, char *argv[]) +{ + char *dir; + int n; + + n = 1; + while (n < argc) { + dir = argv[n++]; + if (rmdir(dir)) { + fprintf(stderr,"%s: %s: %s\n", argv[0], dir, strerror(errno)); + return -1; + } + } + return 0; +} + +rtems_shell_cmd_t rtems_shell_RMDIR_Command = { + "rmdir", /* name */ + "rmdir dir # remove directory", /* usage */ + "files", /* topic */ + rtems_shell_main_rmdir, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_route.c b/cpukit/libmisc/shell/main_route.c new file mode 100644 index 0000000000..ea503b4ef2 --- /dev/null +++ b/cpukit/libmisc/shell/main_route.c @@ -0,0 +1,153 @@ +/* + * ROUTE Command Implmentation + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include <netinet/in.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <net/route.h> + +#include <rtems.h> +#include <rtems/rtems_bsdnet.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_route( + int argc, + char *argv[] +) +{ + int cmd; + struct sockaddr_in dst; + struct sockaddr_in gw; + struct sockaddr_in netmask; + int f_host; + int f_gw = 0; + int cur_idx; + int flags; + int rc; + + memset(&dst, 0, sizeof(dst)); + memset(&gw, 0, sizeof(gw)); + memset(&netmask, 0, sizeof(netmask)); + + dst.sin_len = sizeof(dst); + dst.sin_family = AF_INET; + dst.sin_addr.s_addr = inet_addr("0.0.0.0"); + + gw.sin_len = sizeof(gw); + gw.sin_family = AF_INET; + gw.sin_addr.s_addr = inet_addr("0.0.0.0"); + + netmask.sin_len = sizeof(netmask); + netmask.sin_family = AF_INET; + netmask.sin_addr.s_addr = inet_addr("255.255.255.0"); + + if (argc < 2) { + rtems_bsdnet_show_inet_routes(); + return 0; + } + + if (strcmp(argv[1], "add") == 0) { + cmd = RTM_ADD; + } else if (strcmp(argv[1], "del") == 0) { + cmd = RTM_DELETE; + } else { + printf("invalid command: %s\n", argv[1]); + printf("\tit should be 'add' or 'del'\n"); + return -1; + } + + if (argc < 3) { + printf("not enough arguments\n"); + return -1; + } + + if (strcmp(argv[2], "-host") == 0) { + f_host = 1; + } else if (strcmp(argv[2], "-net") == 0) { + f_host = 0; + } else { + printf("Invalid type: %s\n", argv[1]); + printf("\tit should be '-host' or '-net'\n"); + return -1; + } + + if (argc < 4) { + printf("not enough arguments\n"); + return -1; + } + + inet_pton(AF_INET, argv[3], &dst.sin_addr); + + cur_idx = 4; + while(cur_idx < argc) { + if (strcmp(argv[cur_idx], "gw") == 0) { + if ((cur_idx +1) >= argc) { + printf("no gateway address\n"); + return -1; + } + f_gw = 1; + inet_pton(AF_INET, argv[cur_idx + 1], &gw.sin_addr); + cur_idx += 1; + } else if(strcmp(argv[cur_idx], "netmask") == 0) { + if ((cur_idx +1) >= argc) { + printf("no netmask address\n"); + return -1; + } + f_gw = 1; + inet_pton(AF_INET, argv[cur_idx + 1], &netmask.sin_addr); + cur_idx += 1; + } else { + printf("Unknown argument\n"); + return -1; + } + cur_idx += 1; + } + + flags = RTF_STATIC; + if (f_gw != 0) { + flags |= RTF_GATEWAY; + } + if (f_host != 0) { + flags |= RTF_HOST; + } + + rc = rtems_bsdnet_rtrequest( + cmd, + (struct sockaddr *)&dst, + (struct sockaddr *)&gw, + (struct sockaddr *)&netmask, + flags, + NULL + ); + if (rc < 0) { + printf("Error adding route\n"); + } + + return 0; +} + +rtems_shell_cmd_t rtems_shell_ROUTE_Command = { + "route", /* name */ + "TBD", /* usage */ + "network", /* topic */ + rtems_shell_main_route, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_rtc.c b/cpukit/libmisc/shell/main_rtc.c new file mode 100644 index 0000000000..139018eb13 --- /dev/null +++ b/cpukit/libmisc/shell/main_rtc.c @@ -0,0 +1,173 @@ +/** + * @file + * + * Real time clock shell command. + */ + +/* + * Copyright (c) 2009 + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <rtems.h> +#include <rtems/rtc.h> +#include <rtems/error.h> +#include <rtems/shell.h> + +#define RTEMS_RTC_SHELL_ERROR( fmt, ...) \ + do { \ + printf( "error: " fmt "\n", ##__VA_ARGS__); \ + return -1; \ + } while (0) + +#define RTEMS_RTC_SHELL_ERROR_SC( sc, fmt, ...) \ + if ((sc) != RTEMS_SUCCESSFUL) { \ + printf( "error: " fmt ": %s\n", ##__VA_ARGS__, rtems_status_text( sc)); \ + return -1; \ + } + +static const char rtems_rtc_shell_usage [] = + "real time clock read and set\n" + "\n" + "rtc\n" + "\tprints the current time of day\n" + "\n" + "rtc YYYY-MM-DD [HH:MM:SS [TICKS]]\n" + "\tsets the time of day and real time clock"; + +static int rtems_rtc_shell_main( int argc, char **argv) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_time_of_day tod = { + .year = 1988, + .month = 1, + .day = 1, + .hour = 0, + .minute = 0, + .second = 0, + .ticks = 0 + }; + + if (argc == 1) { + sc = rtems_clock_get_tod( &tod); + RTEMS_RTC_SHELL_ERROR_SC( sc, "get time of day"); + + printf( + "%04" PRIu32 "-%02" PRIu32 "-%02" PRIu32 + " %02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 + " %02" PRIu32 "\n", + tod.year, + tod.month, + tod.day, + tod.hour, + tod.minute, + tod.second, + tod.ticks + ); + } else if (argc > 1 && argc < 5) { + int rv = 0; + int fd = 0; + ssize_t n = 0; + uint32_t v [3]; + + if (argc > 1) { + rv = sscanf( + argv [1], + "%04" PRIu32 "-%02" PRIu32 "-%02" PRIu32, + v, + v + 1, + v + 2 + ); + + if (rv == 3) { + tod.year = v [0]; + tod.month = v [1]; + tod.day = v [2]; + } else { + RTEMS_RTC_SHELL_ERROR( "unexpected YYYY-MM-DD input: %s", argv [1]); + } + } + + if (argc > 2) { + rv = sscanf( + argv [2], + "%04" PRIu32 ":%02" PRIu32 ":%02" PRIu32, + v, + v + 1, + v + 2 + ); + + if (rv == 3) { + tod.hour = v [0]; + tod.minute = v [1]; + tod.second = v [2]; + } else { + RTEMS_RTC_SHELL_ERROR( "unexpected HH:MM:SS input: %s", argv [2]); + } + } + + if (argc > 3) { + rv = sscanf( argv [3], "%5" PRIu32, v); + + if (rv == 1) { + tod.ticks = v [0]; + } else { + RTEMS_RTC_SHELL_ERROR( "unexpected TICKS input: %s", argv [3]); + } + } + + sc = rtems_clock_set( &tod); + RTEMS_RTC_SHELL_ERROR_SC( sc, "set time of day"); + + fd = open( RTC_DEVICE_NAME, O_WRONLY); + if (fd < 0) { + perror( "error: open " RTC_DEVICE_NAME); + return -1; + } + + n = write( fd, &tod, sizeof( tod)); + if (n != (ssize_t) sizeof( tod)) { + perror( "error: write to " RTC_DEVICE_NAME); + close( fd); + return -1; + } + + rv = close( fd); + if (rv != 0) { + perror( "error: close " RTC_DEVICE_NAME); + return -1; + } + } else { + puts( rtems_rtc_shell_usage); + return -1; + } + + return 0; +} + +struct rtems_shell_cmd_tt rtems_shell_RTC_Command = { + .name = "rtc", + .usage = rtems_rtc_shell_usage, + .topic = "misc", + .command = rtems_rtc_shell_main, + .alias = NULL, + .next = NULL +}; diff --git a/cpukit/libmisc/shell/main_setenv.c b/cpukit/libmisc/shell/main_setenv.c new file mode 100644 index 0000000000..5b7c92920f --- /dev/null +++ b/cpukit/libmisc/shell/main_setenv.c @@ -0,0 +1,72 @@ +/* + * Set an environment vairable. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_setenv(int argc, char *argv[]) +{ + char* env = NULL; + char* string = NULL; + int len = 0; + int arg; + char* p; + + if (argc <= 2) { + printf("error: no variable or string\n"); + return 1; + } + + env = argv[1]; + + for (arg = 2; arg < argc; arg++) + len += strlen(argv[arg]); + + len += argc - 2 - 1; + + string = malloc(len + 1); + + if (!string) { + printf("error: no memory\n"); + return 1; + } + + for (arg = 2, p = string; arg < argc; arg++) { + strcpy(p, argv[arg]); + p += strlen(argv[arg]); + if (arg < (argc - 1)) { + *p = ' '; + p++; + } + } + + if (setenv(env, string, 1) < 0) { + printf( "error: %s\n", strerror(errno) ); + free( string ); + return 1; + } + + free( string ); + return 0; +} + +rtems_shell_cmd_t rtems_shell_SETENV_Command = { + "setenv", /* name */ + "setenv [var] [string]", /* usage */ + "misc", /* topic */ + rtems_shell_main_setenv, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_sleep.c b/cpukit/libmisc/shell/main_sleep.c new file mode 100644 index 0000000000..2dbb66a996 --- /dev/null +++ b/cpukit/libmisc/shell/main_sleep.c @@ -0,0 +1,74 @@ +/* + * Sleep Shell Command Implmentation + * + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <time.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/stringto.h> +#include "internal.h" + +int rtems_shell_main_sleep( + int argc, + char *argv[] +) +{ + struct timespec delay; + unsigned long tmp; + + if ((argc != 2) && (argc != 3)) { + fprintf( stderr, "%s: Usage seconds [nanoseconds]\n", argv[0] ); + return -1; + } + + /* + * Convert the seconds argument to a number + */ + if ( rtems_string_to_unsigned_long(argv[1], &tmp, NULL, 0) ) { + printf( "Seconds argument (%s) is not a number\n", argv[1] ); + return -1; + } + delay.tv_sec = (time_t) tmp; + + /* + * If the user specified a nanoseconds argument, convert it + */ + delay.tv_nsec = 0; + if (argc == 3) { + if ( rtems_string_to_unsigned_long(argv[2], &tmp, NULL, 0) ) { + printf( "Seconds argument (%s) is not a number\n", argv[1] ); + return -1; + } + delay.tv_nsec = tmp; + } + + /* + * Now sleep as requested. + */ + nanosleep( &delay, NULL ); + return 0; +} + +rtems_shell_cmd_t rtems_shell_SLEEP_Command = { + "sleep", /* name */ + "sleep seconds [nanoseconds]", /* usage */ + "misc", /* topic */ + rtems_shell_main_sleep, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_stackuse.c b/cpukit/libmisc/shell/main_stackuse.c new file mode 100644 index 0000000000..4d1d944ceb --- /dev/null +++ b/cpukit/libmisc/shell/main_stackuse.c @@ -0,0 +1,44 @@ +/* + * stackuse Command Implementation + * + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> + +#include <rtems.h> +#include <rtems/stackchk.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_stackuse( + int argc __attribute__((unused)), + char *argv[] __attribute__((unused)) +) +{ + rtems_stack_checker_report_usage_with_plugin( + stdout, + (rtems_printk_plugin_t)fprintf + ); + return 0; +} + +rtems_shell_cmd_t rtems_shell_STACKUSE_Command = { + "stackuse", /* name */ + "print per thread stack usage", /* usage */ + "rtems", /* topic */ + rtems_shell_main_stackuse, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_time.c b/cpukit/libmisc/shell/main_time.c new file mode 100644 index 0000000000..7d6cf557ee --- /dev/null +++ b/cpukit/libmisc/shell/main_time.c @@ -0,0 +1,84 @@ +/* + * Time Shell Command Implmentation + * + * Author: Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_time( + int argc, + char *argv[] +) +{ + rtems_shell_cmd_t* shell_cmd; + int errorlevel = 0; + struct timespec start; + struct timespec end; + struct timespec period; + rtems_status_code sc; + + argc--; + + sc = rtems_clock_get_uptime(&start); + if (sc != RTEMS_SUCCESSFUL) + printf("error: cannot read time\n"); + + if (argc) { + shell_cmd = rtems_shell_lookup_cmd(argv[1]); + if ( argv[1] == NULL ) { + errorlevel = -1; + } else if ( shell_cmd == NULL ) { + errorlevel = rtems_shell_script_file(argc, &argv[1]); + } else { + errorlevel = shell_cmd->command(argc, &argv[1]); + } + } + + sc = rtems_clock_get_uptime(&end); + if (sc != RTEMS_SUCCESSFUL) + printf("error: cannot read time\n"); + + period.tv_sec = end.tv_sec - start.tv_sec; + period.tv_nsec = end.tv_nsec - start.tv_nsec; + if (period.tv_nsec < 0) + { + --period.tv_sec; + period.tv_nsec += 1000000000UL; + } + + printf("time: %li:%02li:%02li.%03li\n", + period.tv_sec / 3600, + period.tv_sec / 60, period.tv_sec % 60, + period.tv_nsec / 1000000); + + return errorlevel; +} + +rtems_shell_cmd_t rtems_shell_TIME_Command = { + "time", /* name */ + "time command [arguments...]", /* usage */ + "misc", /* topic */ + rtems_shell_main_time, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_tty.c b/cpukit/libmisc/shell/main_tty.c new file mode 100644 index 0000000000..f8313b55f8 --- /dev/null +++ b/cpukit/libmisc/shell/main_tty.c @@ -0,0 +1,44 @@ +/* + * TTY Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_tty( + int argc __attribute__((unused)), + char *argv[] __attribute__((unused)) +) +{ + printf("%s\n", ttyname(fileno(stdin))); + return 0; +} + +rtems_shell_cmd_t rtems_shell_TTY_Command = { + "tty", /* name */ + "show ttyname", /* usage */ + "misc", /* topic */ + rtems_shell_main_tty, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_umask.c b/cpukit/libmisc/shell/main_umask.c new file mode 100644 index 0000000000..8bfbe7e443 --- /dev/null +++ b/cpukit/libmisc/shell/main_umask.c @@ -0,0 +1,62 @@ +/* + * UMASK Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/stringto.h> +#include "internal.h" + +int rtems_shell_main_umask( + int argc, + char *argv[] +) +{ + unsigned long tmp; + mode_t msk = umask(0); + + if (argc == 2) { + if ( rtems_string_to_unsigned_long(argv[1], &tmp, NULL, 0) ) { + printf( "Mask argument (%s) is not a number\n", argv[1] ); + return -1; + } + msk = (mode_t) tmp; + + } + umask(msk); + + msk = umask(0); + printf("0%o\n", (unsigned int) msk); + umask(msk); + return 0; +} + +rtems_shell_cmd_t rtems_shell_UMASK_Command = { + "umask", /* name */ + "umask [new_umask]", /* usage */ + "misc", /* topic */ + rtems_shell_main_umask, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_unmount.c b/cpukit/libmisc/shell/main_unmount.c new file mode 100644 index 0000000000..ba101dc014 --- /dev/null +++ b/cpukit/libmisc/shell/main_unmount.c @@ -0,0 +1,74 @@ +/* + * Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/shellconfig.h> +#include <rtems/fsmount.h> +#include "internal.h" + +int rtems_shell_main_unmount( + int argc, + char *argv[] +) +{ + char* mount_point = NULL; + int arg; + + for (arg = 1; arg < argc; arg++) { + if (!mount_point) + mount_point = argv[arg]; + else { + fprintf (stderr, "error: only one mount path require: %s\n", argv[arg]); + return 1; + } + } + + if (!mount_point) { + fprintf (stderr, "error: no mount point\n"); + return 1; + } + + /* + * Unmount the disk. + */ + + if (unmount (mount_point) < 0) { + fprintf (stderr, "error: unmount failed: %s: %s\n", + mount_point, strerror (errno)); + return 1; + } + + printf ("unmounted %s\n", mount_point); + + return 0; +} + +rtems_shell_cmd_t rtems_shell_UNMOUNT_Command = { + "unmount", /* name */ + "unmount path # unmount disk", /* usage */ + "files", /* topic */ + rtems_shell_main_unmount, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_unsetenv.c b/cpukit/libmisc/shell/main_unsetenv.c new file mode 100644 index 0000000000..8805ba4341 --- /dev/null +++ b/cpukit/libmisc/shell/main_unsetenv.c @@ -0,0 +1,43 @@ +/* + * Unset an environment vairable. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_unsetenv(int argc, char *argv[]) +{ + if (argc != 2) + { + printf ("error: only argument is the variable name\n"); + return 1; + } + + if (unsetenv (argv[1]) < 0) + { + printf ("error: %s\n", strerror (errno)); + return 1; + } + + return 0; +} + +rtems_shell_cmd_t rtems_shell_UNSETENV_Command = { + "unsetenv", /* name */ + "unsetenv [var]", /* usage */ + "misc", /* topic */ + rtems_shell_main_unsetenv, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_whoami.c b/cpukit/libmisc/shell/main_whoami.c new file mode 100644 index 0000000000..ce34ed95ca --- /dev/null +++ b/cpukit/libmisc/shell/main_whoami.c @@ -0,0 +1,48 @@ +/* + * WHOAMI Shell Command Implmentation + * + * Author: Fernando RUIZ CASAS + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <pwd.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +int rtems_shell_main_whoami( + int argc __attribute__((unused)), + char *argv[] __attribute__((unused)) +) +{ + struct passwd *pwd; + + pwd = getpwuid(geteuid()); + printf( "%s\n", (pwd) ? pwd->pw_name : "nobody"); + return 0; +} + +rtems_shell_cmd_t rtems_shell_WHOAMI_Command = { + "whoami", /* name */ + "show current user", /* usage */ + "misc", /* topic */ + rtems_shell_main_whoami, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/main_wkspaceinfo.c b/cpukit/libmisc/shell/main_wkspaceinfo.c new file mode 100644 index 0000000000..fd7c531854 --- /dev/null +++ b/cpukit/libmisc/shell/main_wkspaceinfo.c @@ -0,0 +1,59 @@ +/* + * MALLOC_INFO Shell Command Implmentation + * + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#include <rtems.h> +#include <rtems/malloc.h> +#include <rtems/shell.h> +#include <rtems/score/protectedheap.h> +#include "internal.h" + +extern bool rtems_unified_work_area; + +void rtems_shell_print_unified_work_area_message(void) +{ + printf( "\nC Program Heap and RTEMS Workspace are %s.\n", + ((rtems_unified_work_area) ? "the same" : "separate") + ); +} + +int rtems_shell_main_wkspace_info( + int argc __attribute__((unused)), + char *argv[] __attribute__((unused)) +) +{ + Heap_Information_block info; + + rtems_shell_print_unified_work_area_message(); + + _Protected_heap_Get_information( &_Workspace_Area, &info ); + rtems_shell_print_heap_info( "free", &info.Free ); + rtems_shell_print_heap_info( "used", &info.Used ); + + return 0; +} + +rtems_shell_cmd_t rtems_shell_WKSPACE_INFO_Command = { + "wkspace", /* name */ + "Report on RTEMS Executive Workspace", /* usage */ + "rtems", /* topic */ + rtems_shell_main_wkspace_info, /* command */ + NULL, /* alias */ + NULL /* next */ +}; diff --git a/cpukit/libmisc/shell/mknod-pack_dev.c b/cpukit/libmisc/shell/mknod-pack_dev.c new file mode 100644 index 0000000000..043019ad5a --- /dev/null +++ b/cpukit/libmisc/shell/mknod-pack_dev.c @@ -0,0 +1,300 @@ +/* $NetBSD: pack_dev.c,v 1.10 2009/02/13 01:37:23 lukem Exp $ */ + +/*- + * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#if 0 +#include <sys/cdefs.h> +#if !defined(lint) +__RCSID("$NetBSD: pack_dev.c,v 1.10 2009/02/13 01:37:23 lukem Exp $"); +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pack_dev.h" +#endif + +static pack_t pack_netbsd; +static pack_t pack_freebsd; +static pack_t pack_8_8; +static pack_t pack_12_20; +static pack_t pack_14_18; +static pack_t pack_8_24; +static pack_t pack_bsdos; +static int compare_format(const void *, const void *); + +static const char iMajorError[] = "invalid major number"; +static const char iMinorError[] = "invalid minor number"; +static const char tooManyFields[] = "too many fields for format"; + +#define makedev(x,y) rtems_filesystem_make_dev_t(x,y) +#define major(d) rtems_filesystem_dev_major_t(d) +#define minor(d) rtems_filesystem_dev_minor_t(d) + + /* exported */ +portdev_t +pack_native(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev(numbers[0], numbers[1]); + if ((u_long)major(dev) != numbers[0]) + *error = iMajorError; + else if ((u_long)minor(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +static portdev_t +pack_netbsd(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_netbsd(numbers[0], numbers[1]); + if ((u_long)major_netbsd(dev) != numbers[0]) + *error = iMajorError; + else if ((u_long)minor_netbsd(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_freebsd(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) +#define minor_freebsd(x) ((int32_t)(((x) & 0xffff00ff) >> 0)) +#define makedev_freebsd(x,y) ((portdev_t)((((x) << 8) & 0x0000ff00) | \ + (((y) << 0) & 0xffff00ff))) + +static portdev_t +pack_freebsd(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_freebsd(numbers[0], numbers[1]); + if ((u_long)major_freebsd(dev) != numbers[0]) + *error = iMajorError; + if ((u_long)minor_freebsd(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_8_8(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) +#define minor_8_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) +#define makedev_8_8(x,y) ((portdev_t)((((x) << 8) & 0x0000ff00) | \ + (((y) << 0) & 0x000000ff))) + +static portdev_t +pack_8_8(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_8_8(numbers[0], numbers[1]); + if ((u_long)major_8_8(dev) != numbers[0]) + *error = iMajorError; + if ((u_long)minor_8_8(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_12_20(x) ((int32_t)(((x) & 0xfff00000) >> 20)) +#define minor_12_20(x) ((int32_t)(((x) & 0x000fffff) >> 0)) +#define makedev_12_20(x,y) ((portdev_t)((((x) << 20) & 0xfff00000) | \ + (((y) << 0) & 0x000fffff))) + +static portdev_t +pack_12_20(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_12_20(numbers[0], numbers[1]); + if ((u_long)major_12_20(dev) != numbers[0]) + *error = iMajorError; + if ((u_long)minor_12_20(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_14_18(x) ((int32_t)(((x) & 0xfffc0000) >> 18)) +#define minor_14_18(x) ((int32_t)(((x) & 0x0003ffff) >> 0)) +#define makedev_14_18(x,y) ((portdev_t)((((x) << 18) & 0xfffc0000) | \ + (((y) << 0) & 0x0003ffff))) + +static portdev_t +pack_14_18(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_14_18(numbers[0], numbers[1]); + if ((u_long)major_14_18(dev) != numbers[0]) + *error = iMajorError; + if ((u_long)minor_14_18(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_8_24(x) ((int32_t)(((x) & 0xff000000) >> 24)) +#define minor_8_24(x) ((int32_t)(((x) & 0x00ffffff) >> 0)) +#define makedev_8_24(x,y) ((portdev_t)((((x) << 24) & 0xff000000) | \ + (((y) << 0) & 0x00ffffff))) + +static portdev_t +pack_8_24(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_8_24(numbers[0], numbers[1]); + if ((u_long)major_8_24(dev) != numbers[0]) + *error = iMajorError; + if ((u_long)minor_8_24(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_12_12_8(x) ((int32_t)(((x) & 0xfff00000) >> 20)) +#define unit_12_12_8(x) ((int32_t)(((x) & 0x000fff00) >> 8)) +#define subunit_12_12_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) +#define makedev_12_12_8(x,y,z) ((portdev_t)((((x) << 20) & 0xfff00000) | \ + (((y) << 8) & 0x000fff00) | \ + (((z) << 0) & 0x000000ff))) + +static portdev_t +pack_bsdos(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_12_20(numbers[0], numbers[1]); + if ((u_long)major_12_20(dev) != numbers[0]) + *error = iMajorError; + if ((u_long)minor_12_20(dev) != numbers[1]) + *error = iMinorError; + } else if (n == 3) { + dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]); + if ((u_long)major_12_12_8(dev) != numbers[0]) + *error = iMajorError; + if ((u_long)unit_12_12_8(dev) != numbers[1]) + *error = "invalid unit number"; + if ((u_long)subunit_12_12_8(dev) != numbers[2]) + *error = "invalid subunit number"; + } else + *error = tooManyFields; + return (dev); +} + + + /* list of formats and pack functions */ + /* this list must be sorted lexically */ +struct format { + const char *name; + pack_t *pack; +} formats[] = { + {"386bsd", pack_8_8}, + {"4bsd", pack_8_8}, + {"bsdos", pack_bsdos}, + {"freebsd", pack_freebsd}, + {"hpux", pack_8_24}, + {"isc", pack_8_8}, + {"linux", pack_8_8}, + {"native", pack_native}, + {"netbsd", pack_netbsd}, + {"osf1", pack_12_20}, + {"sco", pack_8_8}, + {"solaris", pack_14_18}, + {"sunos", pack_8_8}, + {"svr3", pack_8_8}, + {"svr4", pack_14_18}, + {"ultrix", pack_8_8}, +}; + +static int +compare_format(const void *key, const void *element) +{ + const char *name; + const struct format *format; + + name = key; + format = element; + + return (strcmp(name, format->name)); +} + + +pack_t * +pack_find(const char *name) +{ + struct format *format; + + format = bsearch(name, formats, + sizeof(formats)/sizeof(formats[0]), + sizeof(formats[0]), compare_format); + if (format == 0) + return (NULL); + return (format->pack); +} diff --git a/cpukit/libmisc/shell/mknod-pack_dev.h b/cpukit/libmisc/shell/mknod-pack_dev.h new file mode 100644 index 0000000000..5c6d78af8b --- /dev/null +++ b/cpukit/libmisc/shell/mknod-pack_dev.h @@ -0,0 +1,52 @@ +/* $NetBSD: pack_dev.h,v 1.7 2008/04/28 20:23:09 martin Exp $ */ + +/*- + * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 _PACK_DEV_H +#define _PACK_DEV_H + +#ifdef __CYGWIN__ +typedef __dev32_t portdev_t; +#else +typedef dev_t portdev_t; +#endif +typedef portdev_t pack_t(int, u_long [], const char **); + +static pack_t *pack_find(const char *); +static pack_t pack_native; + +#define major_netbsd(x) ((int32_t)((((x) & 0x000fff00) >> 8))) +#define minor_netbsd(x) ((int32_t)((((x) & 0xfff00000) >> 12) | \ + (((x) & 0x000000ff) >> 0))) +#define makedev_netbsd(x,y) ((dev_t)((((x) << 8) & 0x000fff00) | \ + (((y) << 12) & 0xfff00000) | \ + (((y) << 0) & 0x000000ff))) + +#endif /* _PACK_DEV_H */ diff --git a/cpukit/libmisc/shell/pathnames-mv.h b/cpukit/libmisc/shell/pathnames-mv.h new file mode 100644 index 0000000000..a38e4e9450 --- /dev/null +++ b/cpukit/libmisc/shell/pathnames-mv.h @@ -0,0 +1,40 @@ +/* $NetBSD: pathnames.h,v 1.8 2004/08/19 22:26:07 christos Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + * + * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 + */ + +#ifdef RESCUEDIR +#define _PATH_RM RESCUEDIR "/rm" +#define _PATH_CP RESCUEDIR "/cp" +#else +#define _PATH_RM "/bin/rm" +#define _PATH_CP "/bin/cp" +#endif diff --git a/cpukit/libmisc/shell/print-ls.c b/cpukit/libmisc/shell/print-ls.c new file mode 100644 index 0000000000..c19ce9009c --- /dev/null +++ b/cpukit/libmisc/shell/print-ls.c @@ -0,0 +1,492 @@ +/* $NetBSD: print.c,v 1.40 2004/11/17 17:00:00 mycroft Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if 0 +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)print.c 8.5 (Berkeley) 7/28/94"; +#else +__RCSID("$NetBSD: print.c,v 1.40 2004/11/17 17:00:00 mycroft Exp $"); +#endif +#endif /* not lint */ +#endif + +#include <inttypes.h> + +#include <rtems.h> +#include <rtems/libio.h> + +#include <sys/param.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <grp.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +//#include <tzfile.h> +#include <unistd.h> +//#include <util.h> + +#include "extern-ls.h" + +#define DAYSPERNYEAR ((time_t)365) +#define SECSPERDAY ((time_t)60 * (time_t)60 * (time_t)24) + + +#if RTEMS_REMOVED +extern int termwidth; +#endif + +static int printaname(rtems_shell_ls_globals* globals, FTSENT *, int, int); +static void printlink(rtems_shell_ls_globals* globals, FTSENT *); +static void printtime(rtems_shell_ls_globals* globals, time_t); +static int printtype(u_int); + +#if RTEMS_REMOVED +static time_t now; +#endif + +#define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) + +void +printscol(rtems_shell_ls_globals* globals, DISPLAY *dp) +{ + FTSENT *p; + + for (p = dp->list; p; p = p->fts_link) { + if (IS_NOPRINT(p)) + continue; + (void)printaname(globals, p, dp->s_inode, dp->s_block); + (void)putchar('\n'); + } +} + +void +printlong(rtems_shell_ls_globals* globals, DISPLAY *dp) +{ + struct stat *sp; + FTSENT *p; + NAMES *np; + char buf[20]; //, szbuf[5]; + + now = time(NULL); + + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) { +#if RTEMS_REMOVED + if (f_humanize) { + if ((humanize_number(szbuf, sizeof(szbuf), dp->stotal, + "", HN_AUTOSCALE, + (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) + err(exit_jump, 1, "humanize_number"); + (void)printf("total %s\n", szbuf); + } else { +#endif + (void)printf("total %llu\n", + (long long)(howmany(dp->btotal, blocksize))); +#if RTEMS_REMOVED + } +#endif + } + + for (p = dp->list; p; p = p->fts_link) { + if (IS_NOPRINT(p)) + continue; + sp = p->fts_statp; + if (f_inode) + (void)printf("%*lu ", dp->s_inode, sp->st_ino); + if (f_size && !f_humanize) { + (void)printf("%*llu ", dp->s_block, + (long long)howmany(sp->st_blocks, blocksize)); + } + (void)strmode(sp->st_mode, buf); + np = p->fts_pointer; + (void)printf("%s %*lu ", buf, dp->s_nlink, + (unsigned long)sp->st_nlink); + if (!f_grouponly) + (void)printf("%-*s ", dp->s_user, np->user); + (void)printf("%-*s ", dp->s_group, np->group); + if (f_flags) + (void)printf("%-*s ", dp->s_flags, np->flags); + if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) + (void)printf("%*"PRIu32", %*"PRIu32" ", + dp->s_major, major(sp->st_rdev), dp->s_minor, + minor(sp->st_rdev)); + else +#if RTEMS_REMOVED + if (f_humanize) { + if ((humanize_number(szbuf, sizeof(szbuf), + sp->st_size, "", HN_AUTOSCALE, + (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) + err(1, "humanize_number"); + (void)printf("%*s ", dp->s_size, szbuf); + } else { +#endif + { + unsigned long long size; + if (sp->st_size < 0) + size = sp->st_size * -1; + else + size = sp->st_size; + (void)printf("%*llu ", dp->s_size, size); + } + if (f_accesstime) + printtime(globals, sp->st_atime); + else if (f_statustime) + printtime(globals, sp->st_ctime); + else + printtime(globals, sp->st_mtime); + if (f_octal || f_octal_escape) + (void)safe_print(globals, p->fts_name); + else if (f_nonprint) + (void)printescaped(globals, p->fts_name); + else + (void)printf("%s", p->fts_name); + + if (f_type || (f_typedir && S_ISDIR(sp->st_mode))) + (void)printtype(sp->st_mode); + if (S_ISLNK(sp->st_mode)) + printlink(globals, p); + (void)putchar('\n'); + } +} + +void +printcol(rtems_shell_ls_globals* globals, DISPLAY *dp) +{ + static FTSENT **array; + static int lastentries = -1; + FTSENT *p; + int base, chcnt, col, colwidth, num; + int numcols, numrows, row; + //char szbuf[5]; + + colwidth = dp->maxlen; + if (f_inode) + colwidth += dp->s_inode + 1; + if (f_size) { + if (f_humanize) + colwidth += dp->s_size + 1; + else + colwidth += dp->s_block + 1; + } + if (f_type || f_typedir) + colwidth += 1; + + colwidth += 1; + + if (termwidth < 2 * colwidth) { + printscol(globals, dp); + return; + } + + /* + * Have to do random access in the linked list -- build a table + * of pointers. + */ + if (dp->entries > lastentries) { + lastentries = dp->entries; + if ((array = + realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) { + warn(NULL); + printscol(globals, dp); + } + } + for (p = dp->list, num = 0; p; p = p->fts_link) + if (p->fts_number != NO_PRINT) + array[num++] = p; + + numcols = termwidth / colwidth; + colwidth = termwidth / numcols; /* spread out if possible */ + numrows = num / numcols; + if (num % numcols) + ++numrows; + + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) { +#if RTEMS_REMOVED + if (f_humanize) { + if ((humanize_number(szbuf, sizeof(szbuf), dp->stotal, + "", HN_AUTOSCALE, + (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) + err(1, "humanize_number"); + (void)printf("total %s\n", szbuf); + } else { +#endif + (void)printf("total %llu\n", + (long long)(howmany(dp->btotal, blocksize))); +#if RTEMS_REMOVED + } +#endif + } + for (row = 0; row < numrows; ++row) { + for (base = row, chcnt = col = 0; col < numcols; ++col) { + chcnt = printaname(globals, array[base], dp->s_inode, + f_humanize && 0 ? dp->s_size : dp->s_block); + if ((base += numrows) >= num) + break; + while (chcnt++ < colwidth) + (void)putchar(' '); + } + (void)putchar('\n'); + } +} + +void +printacol(rtems_shell_ls_globals* globals, DISPLAY *dp) +{ + FTSENT *p; + int chcnt, col, colwidth; + int numcols; + //char szbuf[5]; + + colwidth = dp->maxlen; + if (f_inode) + colwidth += dp->s_inode + 1; + if (f_size) { + if (f_humanize) + colwidth += dp->s_size + 1; + else + colwidth += dp->s_block + 1; + } + if (f_type || f_typedir) + colwidth += 1; + + colwidth += 1; + + if (termwidth < 2 * colwidth) { + printscol(globals, dp); + return; + } + + numcols = termwidth / colwidth; + colwidth = termwidth / numcols; /* spread out if possible */ + + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) { +#if RTEMS_REMOVED + if (f_humanize) { + if ((humanize_number(szbuf, sizeof(szbuf), dp->stotal, + "", HN_AUTOSCALE, + (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) + err(1, "humanize_number"); + (void)printf("total %s\n", szbuf); + } else { +#endif + (void)printf("total %llu\n", + (long long)(howmany(dp->btotal, blocksize))); +#if RTEMS_REMOVED + } +#endif + } + chcnt = col = 0; + for (p = dp->list; p; p = p->fts_link) { + if (IS_NOPRINT(p)) + continue; + if (col >= numcols) { + chcnt = col = 0; + (void)putchar('\n'); + } + chcnt = printaname(globals, p, dp->s_inode, + f_humanize && 0 ? dp->s_size : dp->s_block); + while (chcnt++ < colwidth) + (void)putchar(' '); + col++; + } + (void)putchar('\n'); +} + +void +printstream(rtems_shell_ls_globals* globals, DISPLAY *dp) +{ + FTSENT *p; + int col; + int extwidth; + + extwidth = 0; + if (f_inode) + extwidth += dp->s_inode + 1; + if (f_size) { + if (f_humanize) + extwidth += dp->s_size + 1; + else + extwidth += dp->s_block + 1; + } + if (f_type) + extwidth += 1; + + for (col = 0, p = dp->list; p != NULL; p = p->fts_link) { + if (IS_NOPRINT(p)) + continue; + if (col > 0) { + (void)putchar(','), col++; + if (col + 1 + extwidth + p->fts_namelen >= termwidth) + (void)putchar('\n'), col = 0; + else + (void)putchar(' '), col++; + } + col += printaname(globals, p, dp->s_inode, + f_humanize && 0 ? dp->s_size : dp->s_block); + } + (void)putchar('\n'); +} + +/* + * print [inode] [size] name + * return # of characters printed, no trailing characters. + */ +static int +printaname(rtems_shell_ls_globals* globals, + FTSENT *p, int inodefield, int sizefield) +{ + struct stat *sp; + int chcnt; + //char szbuf[5]; + + sp = p->fts_statp; + chcnt = 0; + if (f_inode) + chcnt += printf("%*lu ", inodefield, sp->st_ino); + if (f_size) { +#if RTEMS_REMOVED + if (f_humanize) { + if ((humanize_number(szbuf, sizeof(szbuf), sp->st_size, + "", HN_AUTOSCALE, + (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) + err(1, "humanize_number"); + chcnt += printf("%*s ", sizefield, szbuf); + } else { +#endif + chcnt += printf("%*llu ", sizefield, + (long long)howmany(sp->st_blocks, blocksize)); +#if RTEMS_REMOVED + } +#endif + } + if (f_octal || f_octal_escape) + chcnt += safe_print(globals, p->fts_name); + else if (f_nonprint) + chcnt += printescaped(globals, p->fts_name); + else + chcnt += printf("%s", p->fts_name); + if (f_type || (f_typedir && S_ISDIR(sp->st_mode))) + chcnt += printtype(sp->st_mode); + return (chcnt); +} + +static void +printtime(rtems_shell_ls_globals* globals, time_t ftime) +{ + int i; + char *longstring; + + longstring = ctime(&ftime); + for (i = 4; i < 11; ++i) + (void)putchar(longstring[i]); + +#define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY) + if (f_sectime) + for (i = 11; i < 24; i++) + (void)putchar(longstring[i]); + else if (ftime + SIXMONTHS > now && ftime - SIXMONTHS < now) + for (i = 11; i < 16; ++i) + (void)putchar(longstring[i]); + else { + (void)putchar(' '); + for (i = 20; i < 24; ++i) + (void)putchar(longstring[i]); + } + (void)putchar(' '); +} + +static int +printtype(u_int mode) +{ + switch (mode & S_IFMT) { + case S_IFDIR: + (void)putchar('/'); + return (1); + case S_IFIFO: + (void)putchar('|'); + return (1); + case S_IFLNK: + (void)putchar('@'); + return (1); + case S_IFSOCK: + (void)putchar('='); + return (1); +#if RTEMS_REMOVED + case S_IFWHT: + (void)putchar('%'); + return (1); +#endif + } + if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + (void)putchar('*'); + return (1); + } + return (0); +} + +static void +printlink(rtems_shell_ls_globals* globals, FTSENT *p) +{ + int lnklen; + char name[MAXPATHLEN + 1], path[MAXPATHLEN + 1]; + + if (p->fts_level == FTS_ROOTLEVEL) + (void)snprintf(name, sizeof(name), "%s", p->fts_name); + else + (void)snprintf(name, sizeof(name), + "%s/%s", p->fts_parent->fts_accpath, p->fts_name); + if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) { + (void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno)); + return; + } + path[lnklen] = '\0'; + (void)printf(" -> "); + if (f_octal || f_octal_escape) + (void)safe_print(globals, path); + else if (f_nonprint) + (void)printescaped(globals, path); + else + (void)printf("%s", path); +} diff --git a/cpukit/libmisc/shell/print_heapinfo.c b/cpukit/libmisc/shell/print_heapinfo.c new file mode 100644 index 0000000000..ad6db121d2 --- /dev/null +++ b/cpukit/libmisc/shell/print_heapinfo.c @@ -0,0 +1,37 @@ +/* + * Print Heap Information Structure + * + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include "internal.h" + +void rtems_shell_print_heap_info( + const char *c, + Heap_Information *h +) +{ + printf( + "Number of %s blocks: %" PRId32 "\n" + "Largest %s block: %" PRId32 "\n" + "Total bytes %s: %" PRId32 "\n", + c, h->number, + c, h->largest, + c, h->total + ); +} diff --git a/cpukit/libmisc/shell/pwcache.c b/cpukit/libmisc/shell/pwcache.c new file mode 100644 index 0000000000..47b8e96fc6 --- /dev/null +++ b/cpukit/libmisc/shell/pwcache.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. 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. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)pwcache.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/lib/libc/gen/pwcache.c,v 1.11 2007/01/09 00:27:55 imp Exp $"); + +#include <sys/types.h> + +#include <grp.h> +#include <pwd.h> +#include <stdio.h> +#include <string.h> +//#include <utmp.h> + +#define UT_NAMESIZE 64 + +#define NCACHE 64 /* power of 2 */ +#define MASK (NCACHE - 1) /* bits to store with */ + +const char * +user_from_uid(uid_t uid, int nouser) +{ + static struct ncache { + uid_t uid; + int found; + char name[UT_NAMESIZE + 1]; + } c_uid[NCACHE]; + static int pwopen; + struct passwd *pw; + struct ncache *cp; + + cp = c_uid + (uid & MASK); + if (cp->uid != uid || !*cp->name) { + if (pwopen == 0) { + //setpassent(1); + pwopen = 1; + } + pw = getpwuid(uid); + cp->uid = uid; + if (pw != NULL) { + cp->found = 1; + (void)strncpy(cp->name, pw->pw_name, UT_NAMESIZE); + cp->name[UT_NAMESIZE] = '\0'; + } else { + cp->found = 0; + (void)snprintf(cp->name, UT_NAMESIZE, "%u", uid); + if (nouser) + return (NULL); + } + } + return ((nouser && !cp->found) ? NULL : cp->name); +} + +char * +group_from_gid(gid_t gid, int nogroup) +{ + static struct ncache { + gid_t gid; + int found; + char name[UT_NAMESIZE + 1]; + } c_gid[NCACHE]; + static int gropen; + struct group *gr; + struct ncache *cp; + + cp = c_gid + (gid & MASK); + if (cp->gid != gid || !*cp->name) { + if (gropen == 0) { + //setgroupent(1); + gropen = 1; + } + gr = getgrgid(gid); + cp->gid = gid; + if (gr != NULL) { + cp->found = 1; + (void)strncpy(cp->name, gr->gr_name, UT_NAMESIZE); + cp->name[UT_NAMESIZE] = '\0'; + } else { + cp->found = 0; + (void)snprintf(cp->name, UT_NAMESIZE, "%u", gid); + if (nogroup) + return (NULL); + } + } + return ((nogroup && !cp->found) ? NULL : cp->name); +} + diff --git a/cpukit/libmisc/shell/shell.c b/cpukit/libmisc/shell/shell.c new file mode 100644 index 0000000000..bcc146731b --- /dev/null +++ b/cpukit/libmisc/shell/shell.c @@ -0,0 +1,987 @@ +/* + * + * Instantatiate a new terminal shell. + * + * Author: + * + * WORK: fernando.ruiz@ctv.es + * HOME: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <time.h> + +#include <rtems.h> +#include <rtems/error.h> +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/system.h> +#include <rtems/shell.h> +#include <rtems/shellconfig.h> +#include <rtems/console.h> +#include "internal.h" + +#include <termios.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <pwd.h> + +rtems_shell_env_t rtems_global_shell_env = { + .magic = rtems_build_name('S', 'E', 'N', 'V'), + .devname = CONSOLE_DEVICE_NAME, + .taskname = "SHGL", + .exit_shell = false, + .forever = true, + .errorlevel = -1, + .echo = false, + .cwd = "/", + .input = NULL, + .output = NULL, + .output_append = false, + .wake_on_end = RTEMS_ID_NONE, + .login_check = NULL +}; + +rtems_shell_env_t *rtems_current_shell_env = &rtems_global_shell_env; + +/* + * Initialize the shell user/process environment information + */ +rtems_shell_env_t *rtems_shell_init_env( + rtems_shell_env_t *shell_env_p +) +{ + rtems_shell_env_t *shell_env; + + shell_env = malloc(sizeof(rtems_shell_env_t)); + if ( !shell_env ) + return NULL; + if ( !shell_env_p ) { + *shell_env = rtems_global_shell_env; + } else { + *shell_env = *shell_env_p; + } + shell_env->taskname = NULL; + + return shell_env; +} + +/* + * Completely free a shell_env_t and all associated memory + */ +void rtems_shell_env_free( + void *ptr +) +{ + rtems_shell_env_t *shell_env; + shell_env = (rtems_shell_env_t *) ptr; + + if ( !ptr ) + return; + + if ( shell_env->input ) + free((void *)shell_env->input); + if ( shell_env->output ) + free((void *)shell_env->output); + free( ptr ); +} + +/* + * Get a line of user input with modest features + */ +int rtems_shell_line_editor( + char *cmds[], + int count, + int size, + const char *prompt, + FILE *in, + FILE *out +) +{ + unsigned int extended_key; + int c; + int col; + int last_col; + int output; + char line[size]; + char new_line[size]; + int up; + int cmd = -1; + int inserting = 1; + + output = (out && isatty(fileno(in))); + + col = last_col = 0; + + tcdrain(fileno(in)); + if (out) + tcdrain(fileno(out)); + + if (output && prompt) + fprintf(out, "\r%s", prompt); + + line[0] = 0; + new_line[0] = 0; + + for (;;) { + + if (output) + fflush(out); + + extended_key = rtems_shell_getchar(in); + + if (extended_key == EOF) + return -2; + + c = extended_key & RTEMS_SHELL_KEYS_NORMAL_MASK; + + /* + * Make the extended_key usable as a boolean. + */ + extended_key &= ~RTEMS_SHELL_KEYS_NORMAL_MASK; + + up = 0; + + if (extended_key) + { + switch (c) + { + case RTEMS_SHELL_KEYS_END: + if (output) + fprintf(out, "%s", line + col); + col = (int) strlen (line); + break; + + case RTEMS_SHELL_KEYS_HOME: + if (output) { + if (prompt) + fprintf(out,"\r%s", prompt); + } + col = 0; + break; + + case RTEMS_SHELL_KEYS_LARROW: + if (col > 0) + { + col--; + if (output) + fputc('\b', out); + } + break; + + case RTEMS_SHELL_KEYS_RARROW: + if ((col < size) && (line[col] != '\0')) + { + if (output) + fprintf(out, "%c", line[col]); + col++; + } + break; + + case RTEMS_SHELL_KEYS_UARROW: + if ((cmd >= (count - 1)) || (strlen(cmds[cmd + 1]) == 0)) { + if (output) + fputc('\x7', out); + break; + } + + up = 1; + + /* drop through */ + case RTEMS_SHELL_KEYS_DARROW: + + { + int last_cmd = cmd; + int clen = strlen (line); + + if (prompt) + clen += strlen(prompt); + + if (up) { + cmd++; + } else { + if (cmd < 0) { + if (output) + fprintf(out, "\x7"); + break; + } + else + cmd--; + } + + if ((last_cmd < 0) || (strcmp(cmds[last_cmd], line) != 0)) + memcpy (new_line, line, size); + + if (cmd < 0) + memcpy (line, new_line, size); + else + memcpy (line, cmds[cmd], size); + + col = strlen (line); + + if (output) { + fprintf(out,"\r%*c", clen, ' '); + fprintf(out,"\r%s%s", prompt, line); + } + } + break; + + case RTEMS_SHELL_KEYS_DEL: + if (line[col] != '\0') + { + int end; + int bs; + strcpy (&line[col], &line[col + 1]); + if (output) { + fprintf(out,"\r%s%s ", prompt, line); + end = (int) strlen (line); + for (bs = 0; bs < ((end - col) + 1); bs++) + fputc('\b', out); + } + } + break; + + case RTEMS_SHELL_KEYS_INS: + inserting = inserting ? 0 : 1; + break; + } + } + else + { + switch (c) + { + case 1:/*Control-a*/ + if (output) { + if (prompt) + fprintf(out,"\r%s", prompt); + } + col = 0; + break; + + case 5:/*Control-e*/ + if (output) + fprintf(out, "%s", line + col); + col = (int) strlen (line); + break; + + case 11:/*Control-k*/ + if (line[col]) { + if (output) { + int end = strlen(line); + int bs; + fprintf(out,"%*c", end - col, ' '); + for (bs = 0; bs < (end - col); bs++) + fputc('\b', out); + } + line[col] = '\0'; + } + break; + + case 0x04:/*Control-d*/ + if (strlen(line)) + break; + case EOF: + if (output) + fputc('\n', out); + return -2; + + case '\f': + if (output) { + int end; + int bs; + fputc('\f',out); + fprintf(out,"\r%s%s", prompt, line); + end = (int) strlen (line); + for (bs = 0; bs < (end - col); bs++) + fputc('\b', out); + } + break; + + case '\b': + case '\x7f': + if (col > 0) + { + int bs; + col--; + strcpy (line + col, line + col + 1); + if (output) { + fprintf(out,"\b%s \b", line + col); + for (bs = 0; bs < ((int) strlen (line) - col); bs++) + fputc('\b', out); + } + } + break; + + case '\n': + case '\r': + { + /* + * Process the command. + */ + if (output) + fprintf(out,"\n"); + + /* + * Only process the command if we have a command and it is not + * repeated in the history. + */ + if (strlen(line) == 0) { + cmd = -1; + } else { + if ((cmd < 0) || (strcmp(line, cmds[cmd]) != 0)) { + if (count > 1) + memmove(cmds[1], cmds[0], (count - 1) * size); + memmove (cmds[0], line, size); + cmd = 0; + } + } + } + return cmd; + + default: + if ((col < (size - 1)) && (c >= ' ') && (c <= '~')) { + int end = strlen (line); + if (inserting && (col < end) && (end < size)) { + int ch, bs; + for (ch = end + 1; ch > col; ch--) + line[ch] = line[ch - 1]; + if (output) { + fprintf(out, "%s", line + col); + for (bs = 0; bs < (end - col + 1); bs++) + fputc('\b', out); + } + } + line[col++] = c; + if (col > end) + line[col] = '\0'; + if (output) + fputc(c, out); + } + break; + } + } + } + return -2; +} + +/* ----------------------------------------------- * + * - The shell TASK + * Poor but enough.. + * TODO: Redirection. Tty Signals. ENVVARs. Shell language. + * ----------------------------------------------- */ + +void rtems_shell_init_issue(void) +{ + static bool issue_inited=false; + struct stat buf; + + if (issue_inited) + return; + issue_inited = true; + + /* dummy call to init /etc dir */ + getpwnam("root"); + + if (stat("/etc/issue",&buf)) { + rtems_shell_write_file("/etc/issue", + "\n" + "Welcome to @V\\n" + "Login into @S\\n"); + } + + if (stat("/etc/issue.net",&buf)) { + rtems_shell_write_file("/etc/issue.net", + "\n" + "Welcome to %v\n" + "running on %m\n"); + } +} + +static bool rtems_shell_login(FILE * in,FILE * out) { + FILE *fd; + int c; + time_t t; + + rtems_shell_init_issue(); + setuid(0); + setgid(0); + rtems_current_user_env->euid = + rtems_current_user_env->egid =0; + + if (out) { + if ((rtems_current_shell_env->devname[5]!='p')|| + (rtems_current_shell_env->devname[6]!='t')|| + (rtems_current_shell_env->devname[7]!='y')) { + fd = fopen("/etc/issue","r"); + if (fd) { + while ((c=fgetc(fd))!=EOF) { + if (c=='@') { + switch(c=fgetc(fd)) { + case 'L': + fprintf(out,"%s",rtems_current_shell_env->devname); + break; + case 'B': + fprintf(out,"0"); + break; + case 'T': + case 'D': + time(&t); + fprintf(out,"%s",ctime(&t)); + break; + case 'S': + fprintf(out,"RTEMS"); + break; + case 'V': + fprintf(out,"%s\n%s",_RTEMS_version, _Copyright_Notice); + break; + case '@': + fprintf(out,"@"); + break; + default : + fprintf(out,"@%c",c); + break; + } + } else if (c=='\\') { + switch(c=fgetc(fd)) { + case '\\': fprintf(out,"\\"); break; + case 'b': fprintf(out,"\b"); break; + case 'f': fprintf(out,"\f"); break; + case 'n': fprintf(out,"\n"); break; + case 'r': fprintf(out,"\r"); break; + case 's': fprintf(out," "); break; + case 't': fprintf(out,"\t"); break; + case '@': fprintf(out,"@"); break; + } + } else { + fputc(c,out); + } + } + fclose(fd); + } + } else { + fd = fopen("/etc/issue.net","r"); + if (fd) { + while ((c=fgetc(fd))!=EOF) { + if (c=='%') { + switch(c=fgetc(fd)) { + case 't': + fprintf(out,"%s",rtems_current_shell_env->devname); + break; + case 'h': + fprintf(out,"0"); + break; + case 'D': + fprintf(out," "); + break; + case 'd': + time(&t); + fprintf(out,"%s",ctime(&t)); + break; + case 's': + fprintf(out,"RTEMS"); + break; + case 'm': + fprintf(out,"(" CPU_NAME "/" CPU_MODEL_NAME ")"); + break; + case 'r': + fprintf(out,_RTEMS_version); + break; + case 'v': + fprintf(out,"%s\n%s",_RTEMS_version,_Copyright_Notice); + break; + case '%':fprintf(out,"%%"); + break; + default: + fprintf(out,"%%%c",c); + break; + } + } else { + fputc(c,out); + } + } + fclose(fd); + } + } + } + + return rtems_shell_login_prompt( + in, + out, + rtems_current_shell_env->devname, + rtems_current_shell_env->login_check + ); +} + +#if defined(SHELL_DEBUG) +void rtems_shell_print_env( + rtems_shell_env_t * shell_env +) +{ + if ( !shell_env ) { + printk( "shell_env is NULL\n" ); + return; + } + printk( "shell_env=%p\n" + "shell_env->magic=0x%08x\t" + "shell_env->devname=%s\n" + "shell_env->taskname=%s\t" + "shell_env->exit_shell=%d\t" + "shell_env->forever=%d\n", + shell_env->magic, + shell_env->devname, + ((shell_env->taskname) ? shell_env->taskname : "NOT SET"), + shell_env->exit_shell, + shell_env->forever + ); +} +#endif + +rtems_task rtems_shell_task(rtems_task_argument task_argument) +{ + rtems_shell_env_t *shell_env = (rtems_shell_env_t*) task_argument; + rtems_id wake_on_end = shell_env->wake_on_end; + rtems_shell_main_loop( shell_env ); + if (wake_on_end != RTEMS_INVALID_ID) + rtems_event_send (wake_on_end, RTEMS_EVENT_1); + rtems_task_delete( RTEMS_SELF ); +} + +#define RTEMS_SHELL_MAXIMUM_ARGUMENTS (128) +#define RTEMS_SHELL_CMD_SIZE (128) +#define RTEMS_SHELL_CMD_COUNT (32) +#define RTEMS_SHELL_PROMPT_SIZE (128) + +bool rtems_shell_main_loop( + rtems_shell_env_t *shell_env_arg +) +{ + rtems_shell_env_t *shell_env; + rtems_shell_cmd_t *shell_cmd; + rtems_status_code sc; + struct termios term; + struct termios previous_term; + char *prompt = NULL; + int cmd; + int cmd_count = 1; /* assume a script and so only 1 command line */ + char *cmds[RTEMS_SHELL_CMD_COUNT]; + char *cmd_argv; + int argc; + char *argv[RTEMS_SHELL_MAXIMUM_ARGUMENTS]; + bool result = true; + bool input_file = false; + int line = 0; + FILE *stdinToClose = NULL; + FILE *stdoutToClose = NULL; + + rtems_shell_initialize_command_set(); + + shell_env = + rtems_current_shell_env = rtems_shell_init_env( shell_env_arg ); + + /* + * @todo chrisj + * Remove the use of task variables. Change to have a single + * allocation per shell and then set into a notepad register + * in the TCB. Provide a function to return the pointer. + * Task variables are a virus to embedded systems software. + */ + sc = rtems_task_variable_add( + RTEMS_SELF, + (void*)&rtems_current_shell_env, + rtems_shell_env_free + ); + if (sc != RTEMS_SUCCESSFUL) { + rtems_error(sc,"rtems_task_variable_add(current_shell_env):"); + return false; + } + + setuid(0); + setgid(0); + + rtems_current_user_env->euid = rtems_current_user_env->egid = 0; + + fileno(stdout); + + /* fprintf( stderr, + "-%s-%s-\n", shell_env->input, shell_env->output ); + */ + + if (shell_env->output && strcmp(shell_env->output, "stdout") != 0) { + if (strcmp(shell_env->output, "stderr") == 0) { + stdout = stderr; + } else if (strcmp(shell_env->output, "/dev/null") == 0) { + fclose (stdout); + } else { + FILE *output = fopen(shell_env_arg->output, + shell_env_arg->output_append ? "a" : "w"); + if (!output) { + fprintf(stderr, "shell: open output %s failed: %s\n", + shell_env_arg->output, strerror(errno)); + return false; + } + stdout = output; + stdoutToClose = output; + } + } + + if (shell_env->input && strcmp(shell_env_arg->input, "stdin") != 0) { + FILE *input = fopen(shell_env_arg->input, "r"); + if (!input) { + fprintf(stderr, "shell: open input %s failed: %s\n", + shell_env_arg->input, strerror(errno)); + return false; + } + stdin = input; + stdinToClose = input; + shell_env->forever = false; + input_file =true; + } + else { + /* make a raw terminal,Linux Manuals */ + if (tcgetattr(fileno(stdin), &previous_term) >= 0) { + term = previous_term; + term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + term.c_oflag &= ~OPOST; + term.c_oflag |= (OPOST|ONLCR); /* But with cr+nl on output */ + term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + term.c_cflag |= CLOCAL | CREAD; + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 0; + if (tcsetattr (fileno(stdin), TCSADRAIN, &term) < 0) { + fprintf(stderr, + "shell:cannot set terminal attributes(%s)\n",shell_env->devname); + } + } + cmd_count = RTEMS_SHELL_CMD_COUNT; + prompt = malloc(RTEMS_SHELL_PROMPT_SIZE); + if (!prompt) + fprintf(stderr, + "shell:cannot allocate prompt memory\n"); + } + + setvbuf(stdin,NULL,_IONBF,0); /* Not buffered*/ + setvbuf(stdout,NULL,_IONBF,0); /* Not buffered*/ + + rtems_shell_initialize_command_set(); + + /* + * Allocate the command line buffers. + */ + cmd_argv = malloc (RTEMS_SHELL_CMD_SIZE); + if (!cmd_argv) { + fprintf(stderr, "no memory for command line buffers\n" ); + } + + cmds[0] = calloc (cmd_count, RTEMS_SHELL_CMD_SIZE); + if (!cmds[0]) { + fprintf(stderr, "no memory for command line buffers\n" ); + } + + if (cmd_argv && cmds[0]) { + + memset (cmds[0], 0, cmd_count * RTEMS_SHELL_CMD_SIZE); + + for (cmd = 1; cmd < cmd_count; cmd++) { + cmds[cmd] = cmds[cmd - 1] + RTEMS_SHELL_CMD_SIZE; + } + + do { + /* Set again root user and root filesystem, side effect of set_priv..*/ + sc = rtems_libio_set_private_env(); + if (sc != RTEMS_SUCCESSFUL) { + rtems_error(sc,"rtems_libio_set_private_env():"); + result = false; + break; + } + + /* + * By using result here, we can fall to the bottom of the + * loop when the connection is dropped during login and + * keep on trucking. + */ + if (shell_env->login_check != NULL) { + result = rtems_shell_login(stdin,stdout); + } else { + result = true; + } + + if (result) { + const char *c; + memset (cmds[0], 0, cmd_count * RTEMS_SHELL_CMD_SIZE); + if (!input_file) { + rtems_shell_cat_file(stdout,"/etc/motd"); + fprintf(stdout, "\n" + "RTEMS SHELL (Ver.1.0-FRC):%s. " \ + __DATE__". 'help' to list commands.\n", + shell_env->devname); + } + + if (input_file) + chdir(shell_env->cwd); + else + chdir("/"); /* XXX: chdir to getpwent homedir */ + + shell_env->exit_shell = false; + + for (;;) { + int cmd; + + /* Prompt section */ + if (prompt) { + rtems_shell_get_prompt(shell_env, prompt, + RTEMS_SHELL_PROMPT_SIZE); + } + + /* getcmd section */ + cmd = rtems_shell_line_editor(cmds, cmd_count, + RTEMS_SHELL_CMD_SIZE, prompt, + stdin, stdout); + + if (cmd == -1) + continue; /* empty line */ + + if (cmd == -2) + break; /*EOF*/ + + line++; + + if (shell_env->echo) + fprintf(stdout, "%d: %s\n", line, cmds[cmd]); + + /* evaluate cmd section */ + c = cmds[cmd]; + while (*c) { + if (!isblank((unsigned char)*c)) + break; + c++; + } + + if (*c == '\0') /* empty line */ + continue; + + if (*c == '#') { /* comment character */ + cmds[cmd][0] = 0; + continue; + } + + if (!strcmp(cmds[cmd],"bye") || !strcmp(cmds[cmd],"exit")) { + fprintf(stdout, "Shell exiting\n" ); + break; + } else if (!strcmp(cmds[cmd],"shutdown")) { /* exit application */ + fprintf(stdout, "System shutting down at user request\n" ); + exit(0); + } + + /* exec cmd section */ + /* TODO: + * To avoid user crash catch the signals. + * Open a new stdio files with posibility of redirection * + * Run in a new shell task background. (unix &) + * Resuming. A little bash. + */ + memcpy (cmd_argv, cmds[cmd], RTEMS_SHELL_CMD_SIZE); + if (!rtems_shell_make_args(cmd_argv, &argc, argv, + RTEMS_SHELL_MAXIMUM_ARGUMENTS)) { + shell_cmd = rtems_shell_lookup_cmd(argv[0]); + if ( argv[0] == NULL ) { + shell_env->errorlevel = -1; + } else if ( shell_cmd == NULL ) { + shell_env->errorlevel = rtems_shell_script_file(argc, argv); + } else { + shell_env->errorlevel = shell_cmd->command(argc, argv); + } + } + + /* end exec cmd section */ + if (shell_env->exit_shell) + break; + } + + fflush( stdout ); + fflush( stderr ); + } + } while (result && shell_env->forever); + + } + + if (cmds[0]) + free (cmds[0]); + if (cmd_argv) + free (cmd_argv); + if (prompt) + free (prompt); + + if (stdinToClose) { + fclose( stdinToClose ); + } else { + if (tcsetattr(fileno(stdin), TCSADRAIN, &previous_term) < 0) { + fprintf( + stderr, + "shell: cannot reset terminal attributes (%s)\n", + shell_env->devname + ); + } + } + if ( stdoutToClose ) + fclose( stdoutToClose ); + return result; +} + +/* ----------------------------------------------- */ +static rtems_status_code rtems_shell_run ( + const char *task_name, + size_t task_stacksize, + rtems_task_priority task_priority, + const char *devname, + bool forever, + bool wait, + const char *input, + const char *output, + bool output_append, + rtems_id wake_on_end, + bool echo, + rtems_shell_login_check_t login_check +) +{ + rtems_id task_id; + rtems_status_code sc; + rtems_shell_env_t *shell_env; + rtems_name name; + + if ( task_name && strlen(task_name) >= 4) + name = rtems_build_name( + task_name[0], task_name[1], task_name[2], task_name[3]); + else + name = rtems_build_name( 'S', 'E', 'N', 'V' ); + + sc = rtems_task_create( + name, + task_priority, + task_stacksize, + RTEMS_PREEMPT | RTEMS_TIMESLICE | RTEMS_NO_ASR, + RTEMS_LOCAL | RTEMS_FLOATING_POINT, + &task_id + ); + if (sc != RTEMS_SUCCESSFUL) { + rtems_error(sc,"creating task %s in shell_init()",task_name); + return sc; + } + + shell_env = rtems_shell_init_env( NULL ); + if ( !shell_env ) { + rtems_error(RTEMS_NO_MEMORY, + "allocating shell_env %s in shell_init()",task_name); + return RTEMS_NO_MEMORY; + } + shell_env->devname = devname; + shell_env->taskname = task_name; + shell_env->exit_shell = false; + shell_env->forever = forever; + shell_env->echo = echo; + shell_env->input = strdup (input); + shell_env->output = strdup (output); + shell_env->output_append = output_append; + shell_env->wake_on_end = wake_on_end; + shell_env->login_check = login_check; + + getcwd(shell_env->cwd, sizeof(shell_env->cwd)); + + sc = rtems_task_start(task_id, rtems_shell_task, + (rtems_task_argument) shell_env); + if (sc != RTEMS_SUCCESSFUL) { + rtems_error(sc,"starting task %s in shell_init()",task_name); + return sc; + } + + if (wait) { + rtems_event_set out; + sc = rtems_event_receive (RTEMS_EVENT_1, RTEMS_WAIT, 0, &out); + } + + return 0; +} + +rtems_status_code rtems_shell_init( + const char *task_name, + size_t task_stacksize, + rtems_task_priority task_priority, + const char *devname, + bool forever, + bool wait, + rtems_shell_login_check_t login_check +) +{ + rtems_id to_wake = RTEMS_ID_NONE; + + if ( wait ) + to_wake = rtems_task_self(); + + return rtems_shell_run( + task_name, /* task_name */ + task_stacksize, /* task_stacksize */ + task_priority, /* task_priority */ + devname, /* devname */ + forever, /* forever */ + wait, /* wait */ + "stdin", /* input */ + "stdout", /* output */ + false, /* output_append */ + to_wake, /* wake_on_end */ + false, /* echo */ + login_check /* login check */ + ); +} + +rtems_status_code rtems_shell_script ( + const char *task_name, + size_t task_stacksize, + rtems_task_priority task_priority, + const char* input, + const char* output, + bool output_append, + bool wait, + bool echo +) +{ + rtems_id current_task = RTEMS_INVALID_ID; + rtems_status_code sc; + + if (wait) { + sc = rtems_task_ident (RTEMS_SELF, RTEMS_LOCAL, ¤t_task); + if (sc != RTEMS_SUCCESSFUL) + return sc; + } + + sc = rtems_shell_run( + task_name, /* task_name */ + task_stacksize, /* task_stacksize */ + task_priority, /* task_priority */ + NULL, /* devname */ + 0, /* forever */ + wait, /* wait */ + input, /* input */ + output, /* output */ + output_append, /* output_append */ + current_task, /* wake_on_end */ + echo, /* echo */ + NULL /* login check */ + ); + if (sc != RTEMS_SUCCESSFUL) + return sc; + + return sc; +} diff --git a/cpukit/libmisc/shell/shell.h b/cpukit/libmisc/shell/shell.h new file mode 100644 index 0000000000..759bf71589 --- /dev/null +++ b/cpukit/libmisc/shell/shell.h @@ -0,0 +1,292 @@ +/** + * @file rtems/shell.h + * + * Instantatiate a new terminal shell. + */ + +/* + * Author: + * + * WORK: fernando.ruiz@ctv.es + * HOME: correo@fernando-ruiz.com + * + * Thanks at: + * Chris Johns + * + * $Id$ + */ + +#ifndef __RTEMS_SHELL_H__ +#define __RTEMS_SHELL_H__ + +#include <rtems.h> +#include <stdio.h> +#include <termios.h> +#include <rtems/fs.h> +#include <rtems/libio.h> +#include <rtems/chain.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Some key labels to define special keys. + */ + +#define RTEMS_SHELL_KEYS_EXTENDED (0x8000) +#define RTEMS_SHELL_KEYS_NORMAL_MASK (0x00ff) +#define RTEMS_SHELL_KEYS_INS (0) +#define RTEMS_SHELL_KEYS_DEL (1) +#define RTEMS_SHELL_KEYS_UARROW (2) +#define RTEMS_SHELL_KEYS_DARROW (3) +#define RTEMS_SHELL_KEYS_LARROW (4) +#define RTEMS_SHELL_KEYS_RARROW (5) +#define RTEMS_SHELL_KEYS_HOME (6) +#define RTEMS_SHELL_KEYS_END (7) +#define RTEMS_SHELL_KEYS_F1 (8) +#define RTEMS_SHELL_KEYS_F2 (9) +#define RTEMS_SHELL_KEYS_F3 (10) +#define RTEMS_SHELL_KEYS_F4 (11) +#define RTEMS_SHELL_KEYS_F5 (12) +#define RTEMS_SHELL_KEYS_F6 (13) +#define RTEMS_SHELL_KEYS_F7 (14) +#define RTEMS_SHELL_KEYS_F8 (15) +#define RTEMS_SHELL_KEYS_F9 (16) +#define RTEMS_SHELL_KEYS_F10 (17) + +typedef bool (*rtems_shell_login_check_t)( + const char * /* user */, + const char * /* passphrase */ +); + +bool rtems_shell_login_prompt( + FILE *in, + FILE *out, + const char *device, + rtems_shell_login_check_t check +); + +bool rtems_shell_login_check( + const char *user, + const char *passphrase +); + +typedef int (*rtems_shell_command_t)(int argc, char **argv); + +struct rtems_shell_cmd_tt; +typedef struct rtems_shell_cmd_tt rtems_shell_cmd_t; + +struct rtems_shell_cmd_tt { + const char *name; + const char *usage; + const char *topic; + rtems_shell_command_t command; + rtems_shell_cmd_t *alias; + rtems_shell_cmd_t *next; +}; + +typedef struct { + const char *name; + const char *alias; +} rtems_shell_alias_t; + +/* + * The return value has RTEMS_SHELL_KEYS_EXTENDED set if the key + * is extended, ie a special key. + */ +unsigned int rtems_shell_getchar(FILE *in); + +rtems_shell_cmd_t * rtems_shell_lookup_cmd(const char *cmd); + +rtems_shell_cmd_t *rtems_shell_add_cmd_struct( + rtems_shell_cmd_t *shell_cmd +); + +rtems_shell_cmd_t * rtems_shell_add_cmd( + const char *cmd, + const char *topic, + const char *usage, + rtems_shell_command_t command +); + +rtems_shell_cmd_t * rtems_shell_alias_cmd( + const char *cmd, + const char *alias +); + +int rtems_shell_make_args( + char *commandLine, + int *argc_p, + char **argv_p, + int max_args +); + +int rtems_shell_cat_file( + FILE *out, + const char *name +); + +void rtems_shell_write_file( + const char *name, + const char *content +); + +int rtems_shell_script_file( + int argc, + char **argv +); + +/** + * Initialise the shell creating tasks to login and run the shell + * sessions. + * + * @param task_name Name of the shell task. + * @param task_stacksize The size of the stack. If 0 the default size is used. + * @param task_priority The priority the shell runs at. + * @param forever Repeat logins. + * @param wait Caller should block until shell exits. + * @param login_check User login check function, NULL disables login checks. + * + */ +rtems_status_code rtems_shell_init( + const char *task_name, + size_t task_stacksize, + rtems_task_priority task_priority, + const char *devname, + bool forever, + bool wait, + rtems_shell_login_check_t login_check +); + +/** + * Run a shell script creating a shell tasks to execute the command under. + * + * @param task_name Name of the shell task. + * @param task_stacksize The size of the stack. If 0 the default size is used. + * @param task_priority The priority the shell runs at. + * @param input The file of commands. Can be 'stdin' to use stdin. + * @param output The output file to write commands to. Can be 'stdout', + * 'stderr' or '/dev/null'. + * @param output_append Append the output to the file or truncate the file. + * Create if it does not exist. + * @param wait Wait for the script to finish. + */ +rtems_status_code rtems_shell_script( + const char *task_name, + size_t task_stacksize, /* 0 default*/ + rtems_task_priority task_priority, + const char *input, + const char *output, + bool output_append, + bool wait, + bool echo +); + +/** + * Private environment associated with each shell instance. + */ +typedef struct { + /** 'S','E','N','V': Shell Environment */ + rtems_name magic; + const char *devname; + const char *taskname; + bool exit_shell; /* logout */ + bool forever; /* repeat login */ + int errorlevel; + bool echo; + char cwd[256]; + const char *input; + const char *output; + bool output_append; + rtems_id wake_on_end; + rtems_shell_login_check_t login_check; +} rtems_shell_env_t; + +bool rtems_shell_main_loop( + rtems_shell_env_t *rtems_shell_env +); + +extern rtems_shell_env_t rtems_global_shell_env; +extern rtems_shell_env_t *rtems_current_shell_env; + +/* + * The types of file systems we can mount. We have them broken out + * out like this so they can be configured by shellconfig.h. The + * mount command needs special treatment due to some file systems + * being dependent on the network stack and some not. If we had + * all possible file systems being included it would force the + * networking stack into the applcation and this may not be + * required. + */ +struct rtems_shell_filesystems_tt; +typedef struct rtems_shell_filesystems_tt rtems_shell_filesystems_t; + +typedef int (*rtems_shell_filesystems_mounter_t)( + const char* driver, + const char* path, + rtems_shell_filesystems_t* fs, + rtems_filesystem_options_t options +); + +struct rtems_shell_filesystems_tt { + rtems_chain_node link; + const char *name; + int driver_needed; + const rtems_filesystem_operations_table *fs_ops; + rtems_shell_filesystems_mounter_t mounter; +}; + +/** + * This method dynamically builds the command line prompt string + * and places it in @a prompt. + * + * @param[in] shell_env is the shell execution environment + * @param[in] prompt is a pointer to a string buffer area + * @param[in] size is length of the prompt buffer area + * + * @return This method fills in the memory pointed to by @a prompt. + * + * @note An application specific implementation can be provided + * by the user. + */ +void rtems_shell_get_prompt( + rtems_shell_env_t *shell_env, + char *prompt, + size_t size +); + +/** + * Helper for the mount command. + * + * @param[in] driver The path to the driver. + * @param[in] path The path to mount on. + * @param[in] fs The file system definition. + * @param[in] options Special file system options. + */ +int rtems_shell_libc_mounter( + const char* driver, + const char* path, + rtems_shell_filesystems_t* fs, + rtems_filesystem_options_t options +); + +/** + * Add a new file system mount configuration to the mount command. + * + * @param[in] fs The file system mount data. + */ +void rtems_shell_mount_add_fsys(rtems_shell_filesystems_t* fs); + +/** + * Delete file system mount configuration from the mount command. + * + * @param[in] fs The file system mount data to remove. + */ +void rtems_shell_mount_del_fsys(rtems_shell_filesystems_t* fs); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cpukit/libmisc/shell/shell_cmdset.c b/cpukit/libmisc/shell/shell_cmdset.c new file mode 100644 index 0000000000..2abaed9242 --- /dev/null +++ b/cpukit/libmisc/shell/shell_cmdset.c @@ -0,0 +1,234 @@ +/* + * + * Shell Command Set Management + * + * Author: + * WORK: fernando.ruiz@ctv.es + * HOME: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <time.h> +#include <termios.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> + + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/shellconfig.h> +#include "internal.h" + +/* + * Common linked list of shell commands. + * + * Because the help report is very long, there is a topic for each command. + * + * Help list the topics + * help [topic] list the commands for the topic + * help [command] help for the command + * + */ + +rtems_shell_cmd_t * rtems_shell_first_cmd; +rtems_shell_topic_t * rtems_shell_first_topic; + +/* + * Find the topic from the set of topics registered. + */ +rtems_shell_topic_t * rtems_shell_lookup_topic(const char * topic) { + rtems_shell_topic_t * shell_topic; + shell_topic=rtems_shell_first_topic; + + while (shell_topic) { + if (!strcmp(shell_topic->topic,topic)) + return shell_topic; + shell_topic=shell_topic->next; + } + return (rtems_shell_topic_t *) NULL; +} + +/* + * Add a new topic to the list of topics + */ +rtems_shell_topic_t * rtems_shell_add_topic(const char * topic) { + rtems_shell_topic_t * current,*aux; + + if (!rtems_shell_first_topic) { + aux = malloc(sizeof(rtems_shell_topic_t)); + aux->topic = topic; + aux->next = (rtems_shell_topic_t*)NULL; + return rtems_shell_first_topic = aux; + } + current=rtems_shell_first_topic; + if (!strcmp(topic,current->topic)) + return current; + + while (current->next) { + if (!strcmp(topic,current->next->topic)) + return current->next; + current=current->next; + } + aux = malloc(sizeof(rtems_shell_topic_t)); + aux->topic = topic; + aux->next = (rtems_shell_topic_t*)NULL; + current->next = aux; + return aux; +} + +/* + * Find the command in the set + */ +rtems_shell_cmd_t * rtems_shell_lookup_cmd(const char * cmd) { + rtems_shell_cmd_t * shell_cmd; + shell_cmd=rtems_shell_first_cmd; + while (shell_cmd) { + if (!strcmp(shell_cmd->name,cmd)) return shell_cmd; + shell_cmd=shell_cmd->next; + }; + return (rtems_shell_cmd_t *) NULL; +} + +/* + * Add a command structure to the set of known commands + */ +rtems_shell_cmd_t *rtems_shell_add_cmd_struct( + rtems_shell_cmd_t *shell_cmd +) +{ + rtems_shell_cmd_t *shell_pvt; + + shell_pvt = rtems_shell_first_cmd; + while (shell_pvt) { + if (strcmp(shell_pvt->name, shell_cmd->name) == 0) + return NULL; + shell_pvt = shell_pvt->next; + } + + if ( !rtems_shell_first_cmd ) { + rtems_shell_first_cmd = shell_cmd; + } else { + shell_pvt = rtems_shell_first_cmd; + while (shell_pvt->next) + shell_pvt = shell_pvt->next; + shell_pvt->next = shell_cmd; + } + rtems_shell_add_topic( shell_cmd->topic ); + return shell_cmd; +} + +/* + * Add a command as a set of arguments to the set and + * allocate the command structure on the fly. + */ +rtems_shell_cmd_t * rtems_shell_add_cmd( + const char *name, + const char *topic, + const char *usage, + rtems_shell_command_t command +) +{ + rtems_shell_cmd_t *shell_cmd = NULL; + char *my_name = NULL; + char *my_topic = NULL; + char *my_usage = NULL; + + /* Reject empty commands */ + if (name == NULL || command == NULL) { + return NULL; + } + + /* Allocate command stucture */ + shell_cmd = (rtems_shell_cmd_t *) malloc(sizeof(rtems_shell_cmd_t)); + if (shell_cmd == NULL) { + return NULL; + } + + /* Allocate strings */ + my_name = strdup(name); + my_topic = strdup(topic); + my_usage = strdup(usage); + + /* Assign values */ + shell_cmd->name = my_name; + shell_cmd->topic = my_topic; + shell_cmd->usage = my_usage; + shell_cmd->command = command; + shell_cmd->alias = NULL; + shell_cmd->next = NULL; + + if (rtems_shell_add_cmd_struct(shell_cmd) == NULL) { + /* Something is wrong, free allocated resources */ + free(my_usage); + free(my_topic); + free(my_name); + free(shell_cmd); + + return NULL; + } + + return shell_cmd; +} + + +void rtems_shell_initialize_command_set(void) +{ + rtems_shell_cmd_t **c; + rtems_shell_alias_t **a; + + for ( c = rtems_shell_Initial_commands ; *c ; c++ ) { + rtems_shell_add_cmd_struct( *c ); + } + + for ( a = rtems_shell_Initial_aliases ; *a ; a++ ) { + rtems_shell_alias_cmd( (*a)->name, (*a)->alias ); + } + + rtems_shell_register_monitor_commands(); +} + +/* ----------------------------------------------- * + * you can make an alias for every command. + * ----------------------------------------------- */ +rtems_shell_cmd_t *rtems_shell_alias_cmd( + const char *cmd, + const char *alias +) +{ + rtems_shell_cmd_t *shell_cmd, *shell_aux; + + shell_aux = (rtems_shell_cmd_t *) NULL; + + if (alias) { + shell_aux = rtems_shell_lookup_cmd(alias); + if (shell_aux != NULL) { + return NULL; + } + shell_cmd = rtems_shell_lookup_cmd(cmd); + if (shell_cmd != NULL) { + shell_aux = rtems_shell_add_cmd( + alias, + shell_cmd->topic, + shell_cmd->usage, + shell_cmd->command + ); + if (shell_aux) + shell_aux->alias = shell_cmd; + } + } + return shell_aux; +} diff --git a/cpukit/libmisc/shell/shell_getchar.c b/cpukit/libmisc/shell/shell_getchar.c new file mode 100644 index 0000000000..b214a9511f --- /dev/null +++ b/cpukit/libmisc/shell/shell_getchar.c @@ -0,0 +1,176 @@ +/* + * + * Handle keys for the shell. + * + * Author: + * + * Chris Johns (chrisj@rtems.org) + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <time.h> + +#include <rtems.h> +#include <rtems/error.h> +#include <rtems/system.h> +#include <rtems/shell.h> +#include <rtems/shellconfig.h> +#include "internal.h" + +/* + * Taken from the monitor code. + */ + +/* + * Translation tables. Not sure if this is the best way to + * handle this, how-ever I wish to avoid the overhead of + * including a more complete and standard environment such + * as ncurses. + */ + +struct translation_table +{ + char expecting; + const struct translation_table *branch; + unsigned int key; +}; + +static const struct translation_table trans_one[] = +{ + { '\x7e', 0, RTEMS_SHELL_KEYS_HOME }, + { 0, 0, 0 } +}; + +static const struct translation_table trans_two[] = +{ + { '~', 0, RTEMS_SHELL_KEYS_INS }, + { 0, 0, 0 } +}; + +static const struct translation_table trans_three[] = +{ + { '~', 0, RTEMS_SHELL_KEYS_DEL }, + { 0, 0, 0 } +}; + +static const struct translation_table trans_tab_csi[] = +{ + { '1', trans_one, 0 }, + { '2', trans_two, 0 }, + { '3', trans_three, 0 }, + { 'A', 0, RTEMS_SHELL_KEYS_UARROW }, + { 'B', 0, RTEMS_SHELL_KEYS_DARROW }, + { 'D', 0, RTEMS_SHELL_KEYS_LARROW }, + { 'C', 0, RTEMS_SHELL_KEYS_RARROW }, + { 'F', 0, RTEMS_SHELL_KEYS_END }, + { 'H', 0, RTEMS_SHELL_KEYS_HOME }, + { 0, 0, 0 } +}; + +static const struct translation_table trans_tab_O[] = +{ + { '1', 0, RTEMS_SHELL_KEYS_F1 }, + { '2', 0, RTEMS_SHELL_KEYS_F2 }, + { '3', 0, RTEMS_SHELL_KEYS_F3 }, + { '4', 0, RTEMS_SHELL_KEYS_F4 }, + { '5', 0, RTEMS_SHELL_KEYS_F5 }, + { '6', 0, RTEMS_SHELL_KEYS_F6 }, + { '7', 0, RTEMS_SHELL_KEYS_F7 }, + { '8', 0, RTEMS_SHELL_KEYS_F8 }, + { '9', 0, RTEMS_SHELL_KEYS_F9 }, + { ':', 0, RTEMS_SHELL_KEYS_F10 }, + { 'F', 0, RTEMS_SHELL_KEYS_END }, + { 'P', 0, RTEMS_SHELL_KEYS_F1 }, + { 'Q', 0, RTEMS_SHELL_KEYS_F2 }, + { 'R', 0, RTEMS_SHELL_KEYS_F3 }, + { 'S', 0, RTEMS_SHELL_KEYS_F4 }, + { 'T', 0, RTEMS_SHELL_KEYS_F5 }, + { 'U', 0, RTEMS_SHELL_KEYS_F6 }, + { 'V', 0, RTEMS_SHELL_KEYS_F7 }, + { 'W', 0, RTEMS_SHELL_KEYS_F8 }, + { 'X', 0, RTEMS_SHELL_KEYS_F9 }, + { 'Y', 0, RTEMS_SHELL_KEYS_F10 }, + { 0, 0, 0 } +}; + +static const struct translation_table trans_tab[] = +{ + { '[', trans_tab_csi, 0 }, /* CSI command sequences */ + { 'O', trans_tab_O, 0 }, /* O are the fuction keys */ + { 0, 0, 0 } +}; + +/* + * Perform a basic tranlation for some ANSI/VT100 key codes. + * This code could do with a timeout on the ESC as it is + * now lost from the input stream. It is not* used by the + * line editor below so considiered not worth the effort. + */ + +unsigned int +rtems_shell_getchar (FILE *in) +{ + const struct translation_table *translation = 0; + for (;;) + { + int c = fgetc (in); + if (c == EOF) + return EOF; + if (c == 27) + translation = trans_tab; + else + { + /* + * If no translation happing just pass through + * and return the key. + */ + if (translation) + { + /* + * Scan the current table for the key, and if found + * see if this key is a fork. If so follow it and + * wait else return the extended key. + */ + int index = 0; + int branched = 0; + while ((translation[index].expecting != '\0') || + (translation[index].key != '\0')) + { + if (translation[index].expecting == c) + { + /* + * A branch is take if more keys are to come. + */ + if (translation[index].branch == 0) + return RTEMS_SHELL_KEYS_EXTENDED | translation[index].key; + else + { + translation = translation[index].branch; + branched = 1; + break; + } + } + index++; + } + /* + * Who knows what these keys are, just drop them. + */ + if (!branched) + translation = 0; + } + else + return c; + } + } +} + diff --git a/cpukit/libmisc/shell/shell_getprompt.c b/cpukit/libmisc/shell/shell_getprompt.c new file mode 100644 index 0000000000..08c628b219 --- /dev/null +++ b/cpukit/libmisc/shell/shell_getprompt.c @@ -0,0 +1,49 @@ +/* + * Dynamically build the shell prompt + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <time.h> + +#include <rtems.h> +#include <rtems/error.h> +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/system.h> +#include <rtems/shell.h> +#include "internal.h" + +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <pwd.h> + +void rtems_shell_get_prompt( + rtems_shell_env_t *shell_env, + char *prompt, + size_t size +) +{ + char curdir[256]; + + /* XXX: show_prompt user adjustable */ + getcwd(curdir,sizeof(curdir)); + snprintf(prompt, size - 1, "%s%s[%s] %c ", + ((shell_env->taskname) ? shell_env->taskname : ""), + ((shell_env->taskname) ? " " : ""), + curdir, + geteuid()?'$':'#'); +} diff --git a/cpukit/libmisc/shell/shell_makeargs.c b/cpukit/libmisc/shell/shell_makeargs.c new file mode 100644 index 0000000000..727ea829c9 --- /dev/null +++ b/cpukit/libmisc/shell/shell_makeargs.c @@ -0,0 +1,68 @@ +/* + * Split string into argc/argv style argument list + * + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <ctype.h> + +int rtems_shell_make_args( + char *commandLine, + int *argc_p, + char **argv_p, + int max_args +) +{ + int argc; + char *ch; + int status = 0; + + argc = 0; + ch = commandLine; + + while ( *ch ) { + + while ( isspace((unsigned char)*ch) ) ch++; + + if ( *ch == '\0' ) + break; + + if ( *ch == '"' ) { + argv_p[ argc ] = ++ch; + while ( ( *ch != '\0' ) && ( *ch != '"' ) ) ch++; + } else { + argv_p[ argc ] = ch; + while ( ( *ch != '\0' ) && ( !isspace((unsigned char)*ch) ) ) ch++; + } + + argc++; + + if ( *ch == '\0' ) + break; + + *ch++ = '\0'; + + if ( argc == (max_args-1) ) { + status = -1; + break; + } + + + } + argv_p[ argc ] = NULL; + *argc_p = argc; + return status; +} + diff --git a/cpukit/libmisc/shell/shell_script.c b/cpukit/libmisc/shell/shell_script.c new file mode 100644 index 0000000000..8854f4af35 --- /dev/null +++ b/cpukit/libmisc/shell/shell_script.c @@ -0,0 +1,351 @@ +/* + * Shell Script Invocation + * + * Pseudo-code from Chris Johns, implemented and debugged + * by Joel Sherrill. + * + * COPYRIGHT (c) 1989-2009. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <inttypes.h> +#include <sys/types.h> +#include <unistd.h> +#define __need_getopt_newlib +#include <getopt.h> + +#include <rtems.h> +#include <rtems/shell.h> +#include <rtems/stringto.h> +#include "internal.h" + +static void rtems_shell_joel_usage(void) +{ + printf( + "joel [args] where args may be:\n" + " -o FILE output file (default=stdout)\n" + " -p PRIORITY task priority\n" + " -s SIZE task stack size\n" + " -t NAME task name\n" + ); +} + +static int findOnPATH( + const char *userScriptName, + char *scriptFile, + size_t scriptFileLength +) +{ + int sc; + + /* + * If the user script name starts with a / assume it is a fully + * qualified path name and just use it. + */ + if ( userScriptName[0] == '/' ) { + strncpy( scriptFile, userScriptName, PATH_MAX ); + } else { + /* + * For now, the provided name is just turned into a fully + * qualified path name and used. There is no attempt to + * search along a path for it. + */ + + /* XXX should use strncat but what is the limit? */ + getcwd( scriptFile, PATH_MAX ); + strncat( scriptFile, "/", PATH_MAX ); + strncat( + scriptFile, + ( (userScriptName[0] == '.' && userScriptName[1] == '/') ? + &userScriptName[2] : userScriptName), + PATH_MAX + ); + } + + sc = access( scriptFile, R_OK ); + if ( sc ) { + return -1; + } + + return 0; + +#if 0 + /* + * Does the command (argv[0]) contain a path ?, i.e. starts with + * '.' or contains a '/'? + */ + /* TODO: Add concept of PATH */ + if (!contains_path) { + /* check PATH environment variable */ + for (path_part = PATH; path_part; skip to ':') + { + } + if (not found) + return -1; + } +#endif +} + +int rtems_shell_main_joel( + int argc, + char **argv +); + +int rtems_shell_main_joel( + int argc, + char **argv +) +{ + unsigned long tmp; + int option; + int sc; + int verbose = 0; + char *taskName = "JOEL"; + uint32_t stackSize = RTEMS_MINIMUM_STACK_SIZE * 10; + rtems_task_priority taskPriority = 20; + char *outputFile = "stdout"; + rtems_status_code result; + char scriptFile[PATH_MAX]; + struct getopt_data getopt_reent; + + memset(&getopt_reent, 0, sizeof(getopt_data)); + while ( (option = getopt_r( argc, argv, "o:p:s:t:v", &getopt_reent)) != -1 ) { + switch ((char)option) { + case 'o': + outputFile = getopt_reent.optarg; + break; + case 'p': { + const char *s = getopt_reent.optarg; + + if ( rtems_string_to_unsigned_long( s, &tmp, NULL, 0) ) { + printf( "Task Priority argument (%s) is not a number\n", s ); + return -1; + } + taskPriority = (rtems_task_priority) tmp; + break; + } + case 's': { + const char *s = getopt_reent.optarg; + + if ( rtems_string_to_unsigned_long( s, &tmp, NULL, 0) ) { + printf( "Stack size argument (%s) is not a number\n", s ); + return -1; + } + stackSize = (uint32_t) tmp; + break; + } + case 't': + taskName = getopt_reent.optarg; + break; + case 'v': + verbose = 1; + break; + case '?': + default: + rtems_shell_joel_usage(); + return -1; + } + } + + if ( verbose ) { + fprintf( stderr, + "outputFile: %s\n" + "taskPriority: %" PRId32 "\n" + "stackSize: %" PRId32 "\n" + "taskName: %s\n", + outputFile, + taskPriority, + stackSize, + taskName + ); + } + + /* + * Verify there is a script name past the end of the arguments. + * Preincrement to skip program name. + */ + if ( getopt_reent.optind >= argc ) { + fprintf( stderr, "Shell: No script to execute\n" ); + return -1; + } + + /* + * Find script on the path. + * + * NOTE: It is terrible that this is done twice but it + * seems to be the most expedient thing. + */ + sc = findOnPATH( argv[getopt_reent.optind], scriptFile, PATH_MAX ); + if ( sc ) { + fprintf( stderr, "%s: command not found\n", argv[0] ); + return -1; + } + + /* fprintf( stderr, "SCRIPT: -%s-\n", scriptFile ); */ + + /* + * I assume that argv[optind...] will have the arguments to + * the shell script. But that remains to be implemented. + */ + + /* + * Run the script + */ + result = rtems_shell_script( + taskName, /* the name of the task */ + stackSize, /* stack size */ + taskPriority, /* task priority */ + scriptFile, /* the script file */ + outputFile, /* where to redirect the script */ + 0, /* run once and exit */ + 1, /* we will wait */ + verbose /* do we echo */ + ); + if (result) + return -1; + return 0; +} + +rtems_shell_cmd_t rtems_shell_JOEL_Command = { + "joel", /* name */ + "joel [args] SCRIPT", /* usage */ + "misc", /* topic */ + rtems_shell_main_joel, /* command */ + NULL, /* alias */ + NULL /* next */ +}; + +/* + * This is a helper function which takes a command as arguments + * which has not been located as a built-in command and attempts + * to find something in the filesystem with the same name that + * appears to be a shell script. + */ +int rtems_shell_script_file( + int argc __attribute__((unused)), + char *argv[] +) +{ + #define FIRST_LINE_LENGTH 128 + #define SCRIPT_ARGV_LIMIT 32 + char scriptFile[PATH_MAX]; + char *scriptHead; + char scriptHeadBuffer[FIRST_LINE_LENGTH]; + int sc; + FILE *script; + size_t length; + int scriptArgc; + char *scriptArgv[SCRIPT_ARGV_LIMIT]; + + /* + * Clear argv pointer array + */ + for ( scriptArgc=0 ; scriptArgc<SCRIPT_ARGV_LIMIT ; scriptArgc++ ) + scriptArgv[scriptArgc] = NULL; + + /* + * Find argv[0] on the path + */ + sc = findOnPATH( argv[0], scriptFile, PATH_MAX ); + if ( sc ) { + fprintf( stderr, "%s: command not found\n", argv[0] ); + return -1; + } + + /* + * Open the file so we can see if it looks like a script. + */ + script = fopen( scriptFile, "r" ); + if ( !script ) { + fprintf( stderr, "%s: Unable to open %s\n", argv[0], scriptFile ); + return -1; + } + + /* + * Is the script OK to run? + * Verify the current user has permission to execute it. + * + * NOTE: May not work on all file systems + */ + sc = access( scriptFile, X_OK ); + if ( sc ) { + fprintf( stderr, "Unable to execute %s\n", scriptFile ); + fclose( script ); + return -1; + } + + /* + * Try to read the first line from the potential script file + */ + scriptHead = fgets(scriptHeadBuffer, FIRST_LINE_LENGTH, script); + if ( !scriptHead ) { + fprintf( + stderr, "%s: Unable to read first line of %s\n", argv[0], scriptFile ); + fclose( script ); + return -1; + } + + fclose(script); + + length = strnlen(scriptHead, FIRST_LINE_LENGTH); + scriptHead[length - 1] = '\0'; + + /* fprintf( stderr, "FIRST LINE: -%s-\n", scriptHead ); */ + + /* + * Verify the name of the "shell" is joel. This means + * the line starts with "#! joel". + */ + if (strncmp("#! joel", scriptHead, 7) != 0) { + fprintf( stderr, "%s: Not a joel script %s\n", argv[0], scriptFile ); + return -1; + } + + /* + * Do not worry about search path further. We have found the + * script, it is executable, and we have successfully read the + * first line and found out it is a script. + */ + + /* + * Check for arguments in the first line of the script. This changes + * how the shell task is run. + */ + + sc = rtems_shell_make_args( + &scriptHead[3], + &scriptArgc, + scriptArgv, + SCRIPT_ARGV_LIMIT - 1 + ); + if ( sc ) { + fprintf( + stderr, "%s: Error parsing joel arguments %s\n", argv[0], scriptFile ); + return -1; + } + + scriptArgv[ scriptArgc++ ] = scriptFile; + + /* + * TODO: How do we pass arguments from here to the script? + * At this point, it doesn't matter because we don't + * have any way for a shell script to access them. + */ + return rtems_shell_main_joel( scriptArgc, scriptArgv ); + + return 0; +} diff --git a/cpukit/libmisc/shell/shellconfig.c b/cpukit/libmisc/shell/shellconfig.c new file mode 100644 index 0000000000..0cb0de0b30 --- /dev/null +++ b/cpukit/libmisc/shell/shellconfig.c @@ -0,0 +1,21 @@ +/* + * RTEMS Shell Command Set -- DEFAULT Configuration + * + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define CONFIGURE_SHELL_COMMANDS_INIT +#define CONFIGURE_SHELL_COMMANDS_ALL + +#include <rtems/shellconfig.h> diff --git a/cpukit/libmisc/shell/shellconfig.h b/cpukit/libmisc/shell/shellconfig.h new file mode 100644 index 0000000000..cfc475095e --- /dev/null +++ b/cpukit/libmisc/shell/shellconfig.h @@ -0,0 +1,438 @@ +/* + * RTEMS Shell Command Set Configuration + * + * COPYRIGHT (c) 1989-2007. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef _RTEMS_SHELL_CONFIG_h +#define _RTEMS_SHELL_CONFIG_h + +#include <rtems/shell.h> + +/* + * Externs for all command definition structures + */ +extern rtems_shell_cmd_t rtems_shell_HELP_Command; +extern rtems_shell_cmd_t rtems_shell_ALIAS_Command; +extern rtems_shell_cmd_t rtems_shell_TIME_Command; +extern rtems_shell_cmd_t rtems_shell_LOGOFF_Command; +extern rtems_shell_cmd_t rtems_shell_SETENV_Command; +extern rtems_shell_cmd_t rtems_shell_GETENV_Command; +extern rtems_shell_cmd_t rtems_shell_UNSETENV_Command; + +extern rtems_shell_cmd_t rtems_shell_MDUMP_Command; +extern rtems_shell_cmd_t rtems_shell_WDUMP_Command; +extern rtems_shell_cmd_t rtems_shell_MEDIT_Command; +extern rtems_shell_cmd_t rtems_shell_MFILL_Command; +extern rtems_shell_cmd_t rtems_shell_MMOVE_Command; + +extern rtems_shell_cmd_t rtems_shell_JOEL_Command; +extern rtems_shell_cmd_t rtems_shell_DATE_Command; +extern rtems_shell_cmd_t rtems_shell_ECHO_Command; +extern rtems_shell_cmd_t rtems_shell_SLEEP_Command; +extern rtems_shell_cmd_t rtems_shell_ID_Command; +extern rtems_shell_cmd_t rtems_shell_TTY_Command; +extern rtems_shell_cmd_t rtems_shell_WHOAMI_Command; + +extern rtems_shell_cmd_t rtems_shell_CP_Command; +extern rtems_shell_cmd_t rtems_shell_PWD_Command; +extern rtems_shell_cmd_t rtems_shell_LS_Command; +extern rtems_shell_cmd_t rtems_shell_CHDIR_Command; +extern rtems_shell_cmd_t rtems_shell_MKDIR_Command; +extern rtems_shell_cmd_t rtems_shell_RMDIR_Command; +extern rtems_shell_cmd_t rtems_shell_CHROOT_Command; +extern rtems_shell_cmd_t rtems_shell_CHMOD_Command; +extern rtems_shell_cmd_t rtems_shell_CAT_Command; +extern rtems_shell_cmd_t rtems_shell_MKRFS_Command; +extern rtems_shell_cmd_t rtems_shell_MSDOSFMT_Command; +extern rtems_shell_cmd_t rtems_shell_MSDOSFMT_Alias; +extern rtems_shell_cmd_t rtems_shell_MV_Command; +extern rtems_shell_cmd_t rtems_shell_RM_Command; +extern rtems_shell_cmd_t rtems_shell_LN_Command; +extern rtems_shell_cmd_t rtems_shell_MKNOD_Command; +extern rtems_shell_cmd_t rtems_shell_UMASK_Command; +extern rtems_shell_cmd_t rtems_shell_MOUNT_Command; +extern rtems_shell_cmd_t rtems_shell_UNMOUNT_Command; +extern rtems_shell_cmd_t rtems_shell_BLKSYNC_Command; +extern rtems_shell_cmd_t rtems_shell_FDISK_Command; +extern rtems_shell_cmd_t rtems_shell_DD_Command; +extern rtems_shell_cmd_t rtems_shell_HEXDUMP_Command; +extern rtems_shell_cmd_t rtems_shell_DEBUGRFS_Command; + +extern rtems_shell_cmd_t rtems_shell_RTC_Command; + +extern rtems_shell_cmd_t rtems_shell_HALT_Command; +extern rtems_shell_cmd_t rtems_shell_CPUUSE_Command; +extern rtems_shell_cmd_t rtems_shell_STACKUSE_Command; +extern rtems_shell_cmd_t rtems_shell_PERIODUSE_Command; +extern rtems_shell_cmd_t rtems_shell_WKSPACE_INFO_Command; +extern rtems_shell_cmd_t rtems_shell_MALLOC_INFO_Command; +#if RTEMS_NETWORKING + extern rtems_shell_cmd_t rtems_shell_IFCONFIG_Command; + extern rtems_shell_cmd_t rtems_shell_ROUTE_Command; + extern rtems_shell_cmd_t rtems_shell_NETSTATS_Command; +#endif + +extern rtems_shell_cmd_t *rtems_shell_Initial_commands[]; + +/* + * Extern for alias commands + */ +extern rtems_shell_alias_t rtems_shell_DIR_Alias; +extern rtems_shell_alias_t rtems_shell_CD_Alias; +extern rtems_shell_alias_t rtems_shell_EXIT_Alias; + +extern rtems_shell_alias_t *rtems_shell_Initial_aliases[]; + +/* + * If we are configured to alias a command, then make sure the underlying + * command is configured. + */ + +#if !defined(CONFIGURE_SHELL_COMMANDS_ALL) + #if defined(CONFIGURE_SHELL_COMMANDS_DIR) && \ + !defined(CONFIGURE_SHELL_COMMANDS_LS) + #define CONFIGURE_SHELL_COMMAND_LS + #endif + + #if defined(CONFIGURE_SHELL_COMMANDS_CD) && \ + !defined(CONFIGURE_SHELL_COMMANDS_CHDIR) + #define CONFIGURE_SHELL_COMMAND_CHDIR + #endif + + #if defined(CONFIGURE_SHELL_COMMANDS_EXIT) && \ + !defined(CONFIGURE_SHELL_COMMANDS_LOGOFF) + #define CONFIGURE_SHELL_COMMAND_LOGOFF + #endif +#endif + +#if defined(CONFIGURE_SHELL_COMMANDS_INIT) + rtems_shell_alias_t *rtems_shell_Initial_aliases[] = { + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_DIR)) || \ + defined(CONFIGURE_SHELL_COMMAND_DIR) + &rtems_shell_DIR_Alias, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_CD)) || \ + defined(CONFIGURE_SHELL_COMMAND_CD) + &rtems_shell_CD_Alias, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_EXIT)) || \ + defined(CONFIGURE_SHELL_COMMAND_EXIT) + &rtems_shell_EXIT_Alias, + #endif + + /* + * User defined shell aliases + */ + #if defined(CONFIGURE_SHELL_USER_ALIASES) + CONFIGURE_SHELL_USER_ALIASES, + #endif + NULL + }; + + rtems_shell_cmd_t *rtems_shell_Initial_commands[] = { + /* + * General comamnds that should be present + */ + &rtems_shell_HELP_Command, + &rtems_shell_ALIAS_Command, + &rtems_shell_TIME_Command, + + /* + * Common commands that can be optional + */ + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_JOEL)) || \ + defined(CONFIGURE_SHELL_COMMAND_JOEL) + &rtems_shell_JOEL_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_DATE)) || \ + defined(CONFIGURE_SHELL_COMMAND_DATE) + &rtems_shell_DATE_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_ECHO)) || \ + defined(CONFIGURE_SHELL_COMMAND_ECHO) + &rtems_shell_ECHO_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_SLEEP)) || \ + defined(CONFIGURE_SHELL_COMMAND_SLEEP) + &rtems_shell_SLEEP_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_ID)) || \ + defined(CONFIGURE_SHELL_COMMAND_ID) + &rtems_shell_ID_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_TTY)) || \ + defined(CONFIGURE_SHELL_COMMAND_TTY) + &rtems_shell_TTY_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_WHOAMI)) || \ + defined(CONFIGURE_SHELL_COMMAND_WHOAMI) + &rtems_shell_WHOAMI_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_LOGOFF)) || \ + defined(CONFIGURE_SHELL_COMMAND_LOGOFF) + &rtems_shell_LOGOFF_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_SETENV)) || \ + defined(CONFIGURE_SHELL_COMMAND_SETENV) + &rtems_shell_SETENV_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_GETENV)) || \ + defined(CONFIGURE_SHELL_COMMAND_GETENV) + &rtems_shell_GETENV_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_CRLENV)) || \ + defined(CONFIGURE_SHELL_COMMAND_UNSETENV) + &rtems_shell_UNSETENV_Command, + #endif + + /* + * Memory printing/modification family commands + */ + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_MDUMP)) || \ + defined(CONFIGURE_SHELL_COMMAND_MDUMP) + &rtems_shell_MDUMP_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_WDUMP)) || \ + defined(CONFIGURE_SHELL_COMMAND_WDUMP) + &rtems_shell_WDUMP_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_MEDIT)) || \ + defined(CONFIGURE_SHELL_COMMAND_MEDIT) + &rtems_shell_MEDIT_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_MFILL)) || \ + defined(CONFIGURE_SHELL_COMMAND_MFILL) + &rtems_shell_MFILL_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_MMOVE)) || \ + defined(CONFIGURE_SHELL_COMMAND_MMOVE) + &rtems_shell_MMOVE_Command, + #endif + + /* + * File and directory commands + */ + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_CP)) || \ + defined(CONFIGURE_SHELL_COMMAND_CP) + &rtems_shell_CP_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_PWD)) || \ + defined(CONFIGURE_SHELL_COMMAND_PWD) + &rtems_shell_PWD_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_LS)) || \ + defined(CONFIGURE_SHELL_COMMAND_LS) + &rtems_shell_LS_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_CHDIR)) || \ + defined(CONFIGURE_SHELL_COMMAND_CHDIR) + &rtems_shell_CHDIR_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_MKDIR)) || \ + defined(CONFIGURE_SHELL_COMMAND_MKDIR) + &rtems_shell_MKDIR_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_RMDIR)) || \ + defined(CONFIGURE_SHELL_COMMAND_RMDIR) + &rtems_shell_RMDIR_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_CHROOT)) || \ + defined(CONFIGURE_SHELL_COMMAND_CHROOT) + &rtems_shell_CHROOT_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_CHMOD)) || \ + defined(CONFIGURE_SHELL_COMMAND_CHMOD) + &rtems_shell_CHMOD_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_CAT)) || \ + defined(CONFIGURE_SHELL_COMMAND_CAT) + &rtems_shell_CAT_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_MKRFS)) || \ + defined(CONFIGURE_SHELL_COMMAND_MKRFS) + &rtems_shell_MKRFS_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_MSDOSFMT)) || \ + defined(CONFIGURE_SHELL_COMMAND_MSDOSFMT) + &rtems_shell_MSDOSFMT_Command, + &rtems_shell_MSDOSFMT_Alias, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_MV)) || \ + defined(CONFIGURE_SHELL_COMMAND_MV) + &rtems_shell_MV_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_RM)) || \ + defined(CONFIGURE_SHELL_COMMAND_RM) + &rtems_shell_RM_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_LN)) || \ + defined(CONFIGURE_SHELL_COMMAND_LN) + &rtems_shell_LN_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_MKNOD)) || \ + defined(CONFIGURE_SHELL_COMMAND_MKNOD) + &rtems_shell_MKNOD_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_UMASK)) || \ + defined(CONFIGURE_SHELL_COMMAND_UMASK) + &rtems_shell_UMASK_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_MOUNT)) || \ + defined(CONFIGURE_SHELL_COMMAND_MOUNT) + &rtems_shell_MOUNT_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_UNMOUNT)) || \ + defined(CONFIGURE_SHELL_COMMAND_UNMOUNT) + &rtems_shell_UNMOUNT_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_BLKSYNC)) || \ + defined(CONFIGURE_SHELL_COMMAND_BLKSYNC) + &rtems_shell_BLKSYNC_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_FDISK)) || \ + defined(CONFIGURE_SHELL_COMMAND_FDISK) + &rtems_shell_FDISK_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_DD)) || \ + defined(CONFIGURE_SHELL_COMMAND_DD) + &rtems_shell_DD_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_HEXDUMP)) || \ + defined(CONFIGURE_SHELL_COMMAND_HEXDUMP) + &rtems_shell_HEXDUMP_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_DEBUGRFS)) || \ + defined(CONFIGURE_SHELL_COMMAND_DEBUGRFS) + &rtems_shell_DEBUGRFS_Command, + #endif + + /* + * RTEMS Related commands + */ + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_HALT)) || \ + defined(CONFIGURE_SHELL_COMMAND_HALT) + &rtems_shell_HALT_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_CPUUSE)) || \ + defined(CONFIGURE_SHELL_COMMAND_CPUUSE) + &rtems_shell_CPUUSE_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_STACKUSE)) || \ + defined(CONFIGURE_SHELL_COMMAND_STACKUSE) + &rtems_shell_STACKUSE_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_PERIODUSE)) || \ + defined(CONFIGURE_SHELL_COMMAND_PERIODUSE) + &rtems_shell_PERIODUSE_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_WKSPACE_INFO)) || \ + defined(CONFIGURE_SHELL_COMMAND_WKSPACE_INFO) + &rtems_shell_WKSPACE_INFO_Command, + #endif + + /* + * Malloc family commands + */ + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_MALLOC_INFO)) || \ + defined(CONFIGURE_SHELL_COMMAND_MALLOC_INFO) + &rtems_shell_MALLOC_INFO_Command, + #endif + + /* + * Network related commands + */ + #if RTEMS_NETWORKING + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL_NETWORKING) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_IFCONFIG)) || \ + defined(CONFIGURE_SHELL_COMMAND_IFCONFIG) + &rtems_shell_IFCONFIG_Command, + #endif + + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL_NETWORKING) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_ROUTE)) || \ + defined(CONFIGURE_SHELL_COMMAND_ROUTE) + &rtems_shell_ROUTE_Command, + #endif + + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL_NETWORKING) && \ + !defined(CONFIGURE_SHELL_NO_COMMAND_NETSTATS)) || \ + defined(CONFIGURE_SHELL_COMMAND_NETSTATS) + &rtems_shell_NETSTATS_Command, + #endif + #endif + + /* Miscanellous shell commands */ + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) \ + && !defined(CONFIGURE_SHELL_NO_COMMAND_RTC)) \ + || defined(CONFIGURE_SHELL_COMMAND_RTC) + &rtems_shell_RTC_Command, + #endif + + /* + * User defined shell commands + */ + #if defined(CONFIGURE_SHELL_USER_COMMANDS) + CONFIGURE_SHELL_USER_COMMANDS, + #endif + NULL + }; + +#endif + +#endif diff --git a/cpukit/libmisc/shell/utils-cp.c b/cpukit/libmisc/shell/utils-cp.c new file mode 100644 index 0000000000..7b7c7fc771 --- /dev/null +++ b/cpukit/libmisc/shell/utils-cp.c @@ -0,0 +1,496 @@ +/* $NetBSD: utils.c,v 1.29 2005/10/15 18:22:18 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/cdefs.h> +#if 0 +#ifndef lint +#if 0 +static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; +#else +__RCSID("$NetBSD: utils.c,v 1.29 2005/10/15 18:22:18 christos Exp $"); +#endif +#endif /* not lint */ +#endif + +#if 0 +#include <sys/mman.h> +#endif +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <utime.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "extern-cp.h" + +#define lchmod chmod +#define lchown chown + +#define cp_pct(x, y) ((y == 0) ? 0 : (int)(100.0 * (x) / (y))) + +int +set_utimes(const char *file, struct stat *fs) +{ + struct utimbuf tv; + + tv.actime = fs->st_atime; + tv.modtime = fs->st_mtime; + + if (utime(file, &tv)) { + warn("lutimes: %s", file); + return (1); + } + return (0); +} + +int +copy_file(rtems_shell_cp_globals* cp_globals __attribute__((unused)), FTSENT *entp, int dne) +{ +#define MAX_READ max_read + int max_read; + char* buf; + struct stat *fs; + ssize_t wcount; + size_t wresid; + off_t wtotal; + int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0; + char *bufp; +#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED + char *p; +#endif + + fs = entp->fts_statp; + + max_read = fs->st_blksize; + if (max_read < (8 * 1024)) + max_read = 8 * 1024; + buf = malloc (max_read); + if (!buf) + { + warn("no memory"); + return (1); + } + + if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { + warn("%s", entp->fts_path); + (void)free(buf); + return (1); + } + + /* + * If the file exists and we're interactive, verify with the user. + * If the file DNE, set the mode to be the from file, minus setuid + * bits, modified by the umask; arguably wrong, but it makes copying + * executables work right and it's been that way forever. (The + * other choice is 666 or'ed with the execute bits on the from file + * modified by the umask.) + */ + if (!dne) { +#define YESNO "(y/n [n]) " + if (nflag) { + if (vflag) + printf("%s not overwritten\n", to.p_path); + (void)close(from_fd); + (void)free(buf); + return (0); + } else if (iflag) { + (void)fprintf(stderr, "overwrite %s? %s", + to.p_path, YESNO); + checkch = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (checkch != 'y' && checkch != 'Y') { + (void)close(from_fd); + (void)free(buf); + (void)fprintf(stderr, "not overwritten\n"); + return (1); + } + } + + if (fflag) { + /* remove existing destination file name, + * create a new file */ + (void)unlink(to.p_path); + if (!lflag) + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + } else { + if (!lflag) + /* overwrite existing destination file name */ + to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); + } + } else { + if (!lflag) + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + } + + if (to_fd == -1) { + warn("%s", to.p_path); + (void)close(from_fd); + (void)free(buf); + return (1); + } + + rval = 0; + + if (!lflag) { + /* + * Mmap and write if less than 8M (the limit is so we don't totally + * trash memory on big files. This is really a minor hack, but it + * wins some CPU back. + */ +#ifdef CCJ_REMOVED_VM_AND_BUFFER_CACHE_SYNCHRONIZED + if (S_ISREG(fs->st_mode) && fs->st_size > 0 && + fs->st_size <= 8 * 1048576) { + if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, + MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) { + warn("%s", entp->fts_path); + rval = 1; + } else { + wtotal = 0; + for (bufp = p, wresid = fs->st_size; ; + bufp += wcount, wresid -= (size_t)wcount) { + wcount = write(to_fd, bufp, wresid); + if (wcount <= 0) + break; + wtotal += wcount; + if (info) { + info = 0; + (void)fprintf(stderr, + "%s -> %s %3d%%\n", + entp->fts_path, to.p_path, + cp_pct(wtotal, fs->st_size)); + } + if (wcount >= (ssize_t)wresid) + break; + } + if (wcount != (ssize_t)wresid) { + warn("%s", to.p_path); + rval = 1; + } + /* Some systems don't unmap on close(2). */ + if (munmap(p, fs->st_size) < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + } else +#endif + { + wtotal = 0; + while ((rcount = read(from_fd, buf, MAX_READ)) > 0) { + for (bufp = buf, wresid = rcount; ; + bufp += wcount, wresid -= wcount) { + wcount = write(to_fd, bufp, wresid); + if (wcount <= 0) + break; + wtotal += wcount; + if (info) { + info = 0; + (void)fprintf(stderr, + "%s -> %s %3d%%\n", + entp->fts_path, to.p_path, + cp_pct(wtotal, fs->st_size)); + } + if (wcount >= (ssize_t)wresid) + break; + } + if (wcount != (ssize_t)wresid) { + warn("%s", to.p_path); + rval = 1; + break; + } + } + if (rcount < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + } else { + if (link(entp->fts_path, to.p_path)) { + warn("%s", to.p_path); + rval = 1; + } + } + (void)close(from_fd); + + /* + * Don't remove the target even after an error. The target might + * not be a regular file, or its attributes might be important, + * or its contents might be irreplaceable. It would only be safe + * to remove it if we created it and its length is 0. + */ + + if (!lflag) { + if (pflag && setfile(cp_globals, fs, to_fd)) + rval = 1; + if (pflag && preserve_fd_acls(from_fd, to_fd) != 0) + rval = 1; + (void)close(from_fd); + if (close(to_fd)) { + warn("%s", to.p_path); + rval = 1; + } + } + (void)free(buf); + return (rval); +} + +int +copy_link(rtems_shell_cp_globals* cp_globals, FTSENT *p, int exists) +{ + ssize_t len; + char llink[PATH_MAX]; + + if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) { + warn("readlink: %s", p->fts_path); + return (1); + } + llink[len] = '\0'; + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (symlink(llink, to.p_path)) { + warn("symlink: %s", llink); + return (1); + } + return (pflag ? setfile(cp_globals, p->fts_statp, -1) : 0); +} + +int +copy_fifo(rtems_shell_cp_globals* cp_globals, struct stat *from_stat, int exists) +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mkfifo(to.p_path, from_stat->st_mode)) { + warn("mkfifo: %s", to.p_path); + return (1); + } + return (pflag ? setfile(cp_globals, from_stat, -1) : 0); +} + +int +copy_special(rtems_shell_cp_globals* cp_globals, struct stat *from_stat, int exists) +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { + warn("mknod: %s", to.p_path); + return (1); + } + return (pflag ? setfile(cp_globals, from_stat, -1) : 0); +} + +#define TIMESPEC_TO_TIMEVAL(tv, ts) { \ + (tv)->tv_sec = *(ts); \ + (tv)->tv_usec = 0; \ +} + +#define st_atimespec st_atime +#define st_mtimespec st_mtime +#define lutimes utimes + +int +setfile(rtems_shell_cp_globals* cp_globals, struct stat *fs, int fd) +{ + static struct timeval tv[2]; + struct stat ts; + int rval, gotstat, islink, fdval; + + rval = 0; + fdval = fd != -1; + islink = !fdval && S_ISLNK(fs->st_mode); + fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX | + S_IRWXU | S_IRWXG | S_IRWXO; + + TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); +#if 0 + if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) { + warn("%sutimes: %s", islink ? "l" : "", to.p_path); + rval = 1; + } +#endif + if (fdval ? fstat(fd, &ts) : + (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts))) + gotstat = 0; + else { + gotstat = 1; + ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX | + S_IRWXU | S_IRWXG | S_IRWXO; + } + /* + * Changing the ownership probably won't succeed, unless we're root + * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting + * the mode; current BSD behavior is to remove all setuid bits on + * chown. If chown fails, lose setuid/setgid bits. + */ + if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid) + if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) : + (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) : + chown(to.p_path, fs->st_uid, fs->st_gid))) { + if (errno != EPERM) { + warn("chown: %s", to.p_path); + rval = 1; + } + fs->st_mode &= ~(S_ISUID | S_ISGID); + } + + if (!gotstat || fs->st_mode != ts.st_mode) + if (fdval ? fchmod(fd, fs->st_mode) : + (islink ? lchmod(to.p_path, fs->st_mode) : + chmod(to.p_path, fs->st_mode))) { + warn("chmod: %s", to.p_path); + rval = 1; + } + +#if 0 + if (!gotstat || fs->st_flags != ts.st_flags) + if (fdval ? + fchflags(fd, fs->st_flags) : + (islink ? (errno = ENOSYS) : + chflags(to.p_path, fs->st_flags))) { + warn("chflags: %s", to.p_path); + rval = 1; + } +#endif + + return (rval); +} + +int +preserve_fd_acls(int source_fd __attribute__((unused)), int dest_fd __attribute__((unused))) +{ +#if 0 + struct acl *aclp; + acl_t acl; + + if (fpathconf(source_fd, _PC_ACL_EXTENDED) != 1 || + fpathconf(dest_fd, _PC_ACL_EXTENDED) != 1) + return (0); + acl = acl_get_fd(source_fd); + if (acl == NULL) { + warn("failed to get acl entries while setting %s", to.p_path); + return (1); + } + aclp = &acl->ats_acl; + if (aclp->acl_cnt == 3) + return (0); + if (acl_set_fd(dest_fd, acl) < 0) { + warn("failed to set acl entries for %s", to.p_path); + return (1); + } +#endif + return (0); +} + +int +preserve_dir_acls(struct stat *fs __attribute__((unused)), char *source_dir __attribute__((unused)), char *dest_dir __attribute__((unused))) +{ +#if 0 + acl_t (*aclgetf)(const char *, acl_type_t); + int (*aclsetf)(const char *, acl_type_t, acl_t); + struct acl *aclp; + acl_t acl; + + if (pathconf(source_dir, _PC_ACL_EXTENDED) != 1 || + pathconf(dest_dir, _PC_ACL_EXTENDED) != 1) + return (0); + /* + * If the file is a link we will not follow it + */ + if (S_ISLNK(fs->st_mode)) { + aclgetf = acl_get_link_np; + aclsetf = acl_set_link_np; + } else { + aclgetf = acl_get_file; + aclsetf = acl_set_file; + } + /* + * Even if there is no ACL_TYPE_DEFAULT entry here, a zero + * size ACL will be returned. So it is not safe to simply + * check the pointer to see if the default ACL is present. + */ + acl = aclgetf(source_dir, ACL_TYPE_DEFAULT); + if (acl == NULL) { + warn("failed to get default acl entries on %s", + source_dir); + return (1); + } + aclp = &acl->ats_acl; + if (aclp->acl_cnt != 0 && aclsetf(dest_dir, + ACL_TYPE_DEFAULT, acl) < 0) { + warn("failed to set default acl entries on %s", + dest_dir); + return (1); + } + acl = aclgetf(source_dir, ACL_TYPE_ACCESS); + if (acl == NULL) { + warn("failed to get acl entries on %s", source_dir); + return (1); + } + aclp = &acl->ats_acl; + if (aclsetf(dest_dir, ACL_TYPE_ACCESS, acl) < 0) { + warn("failed to set acl entries on %s", dest_dir); + return (1); + } +#endif + return (0); +} + +void +usage(rtems_shell_cp_globals* cp_globals) +{ + (void)fprintf(stderr, "%s\n%s\n", +"usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-alpv] source_file target_file", +" cp [-R [-H | -L | -P]] [-f | -i | -n] [-alpv] source_file ... " +"target_directory"); + longjmp (cp_globals->exit_jmp, 1); +} diff --git a/cpukit/libmisc/shell/utils-ls.c b/cpukit/libmisc/shell/utils-ls.c new file mode 100644 index 0000000000..7ff0d1b4bd --- /dev/null +++ b/cpukit/libmisc/shell/utils-ls.c @@ -0,0 +1,117 @@ +/* $NetBSD: util.c,v 1.28 2005/06/17 14:36:16 hira Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if 0 +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)util.c 8.5 (Berkeley) 4/28/95"; +#else +__RCSID("$NetBSD: util.c,v 1.28 2005/06/17 14:36:16 hira Exp $"); +#endif +#endif /* not lint */ +#endif + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <fts.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <vis.h> + +#include "extern-ls.h" + +#define SIZE_T_MAX 255 + +int +safe_print(rtems_shell_ls_globals* globals, const char *src) +{ + size_t len; + char *name; + int flags; + + flags = VIS_NL | VIS_OCTAL; + if (f_octal_escape) + flags |= VIS_CSTYLE; + + len = strlen(src); + if (len != 0 && SIZE_T_MAX/len <= 4) { + errx(exit_jump, EXIT_FAILURE, "%s: name too long", src); + /* NOTREACHED */ + } + + name = (char *)malloc(4*len+1); + if (name != NULL) { + len = strvis(name, src, flags); + printf("%s", name); + free(name); + return len; + } else + errx(exit_jump, EXIT_FAILURE, "out of memory!"); + /* NOTREACHED */ +} + +int +printescaped(rtems_shell_ls_globals* globals __attribute__((unused)), const char *src) +{ + unsigned char c; + int n; + + for (n = 0; (c = *src) != '\0'; ++src, ++n) + if (isprint(c)) + (void)putchar(c); + else + (void)putchar('?'); + return n; +} + +void +usage(rtems_shell_ls_globals* globals) +{ + + (void)fprintf(stderr, + "usage: %s [-AaBbCcdFfghikLlmnopqRrSsTtuWwx1] [file ...]\n", + "ls"); + exit(EXIT_FAILURE); + /* NOTREACHED */ +} diff --git a/cpukit/libmisc/shell/verr.c b/cpukit/libmisc/shell/verr.c new file mode 100644 index 0000000000..301e229a1c --- /dev/null +++ b/cpukit/libmisc/shell/verr.c @@ -0,0 +1,75 @@ +/* $NetBSD: verr.c,v 1.13 2005/09/13 01:44:09 christos Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)err.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: verr.c,v 1.13 2005/09/13 01:44:09 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include <err.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef __weak_alias +__weak_alias(verr, _verr) +#endif + +__dead void +verr(jmp_buf* exit_jmp, int eval __attribute__((unused)), const char *fmt, _BSD_VA_LIST_ ap) +{ + int sverrno; + + sverrno = errno; +#if 0 + (void)fprintf(stderr, "%s: ", getprogname()); +#endif + if (fmt != NULL) { + (void)vfprintf(stdout, fmt, ap); + (void)fprintf(stdout, ": "); + } + (void)fprintf(stdout, "%s\n", strerror(sverrno)); + longjmp (*exit_jmp, 1); +} diff --git a/cpukit/libmisc/shell/verrx.c b/cpukit/libmisc/shell/verrx.c new file mode 100644 index 0000000000..30355dd31c --- /dev/null +++ b/cpukit/libmisc/shell/verrx.c @@ -0,0 +1,68 @@ +/* $NetBSD: verrx.c,v 1.13 2005/09/13 01:44:09 christos Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)err.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: verrx.c,v 1.13 2005/09/13 01:44:09 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include <err.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#ifdef __weak_alias +__weak_alias(verrx, _verrx) +#endif + +__dead void +verrx(jmp_buf* exit_jmp, int eval __attribute__((unused)), const char *fmt, _BSD_VA_LIST_ ap) +{ +#if 0 + (void)fprintf(stderr, "%s: ", getprogname()); +#endif + if (fmt != NULL) + (void)vfprintf(stdout, fmt, ap); + (void)fprintf(stdout, "\n"); + longjmp (*exit_jmp, 1); +} diff --git a/cpukit/libmisc/shell/vis.c b/cpukit/libmisc/shell/vis.c new file mode 100644 index 0000000000..6316555279 --- /dev/null +++ b/cpukit/libmisc/shell/vis.c @@ -0,0 +1,387 @@ +/* $NetBSD: vis.c,v 1.33 2005/05/28 13:11:14 lukem Exp $ */ + +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + */ + +/*- + * Copyright (c) 1999, 2005 The NetBSD Foundation, Inc. + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define _DIAGASSERT(a) + +#if 0 +#include <sys/cdefs.h> +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: vis.c,v 1.33 2005/05/28 13:11:14 lukem Exp $"); +#endif /* LIBC_SCCS and not lint */ +#endif + +#include <sys/types.h> + +#include <vis.h> +#include <stdlib.h> + +#if !HAVE_VIS || !HAVE_SVIS +#include <ctype.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> + +#undef BELL +#define BELL '\a' + +#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') +#define iswhite(c) (c == ' ' || c == '\t' || c == '\n') +#define issafe(c) (c == '\b' || c == BELL || c == '\r') +#define xtoa(c) "0123456789abcdef"[c] + +#define MAXEXTRAS 5 + + +#define MAKEEXTRALIST(flag, extra, orig) \ +do { \ + const char *o = orig; \ + char *e; \ + while (*o++) \ + continue; \ + extra = malloc((size_t)((o - orig) + MAXEXTRAS)); \ + if (!extra) break; \ + for (o = orig, e = extra; (*e++ = *o++) != '\0';) \ + continue; \ + e--; \ + if (flag & VIS_SP) *e++ = ' '; \ + if (flag & VIS_TAB) *e++ = '\t'; \ + if (flag & VIS_NL) *e++ = '\n'; \ + if ((flag & VIS_NOSLASH) == 0) *e++ = '\\'; \ + *e = '\0'; \ +} while (/*CONSTCOND*/0) + + +/* + * This is HVIS, the macro of vis used to HTTP style (RFC 1808) + */ +#define HVIS(dst, c, flag, nextc, extra) \ +do \ + if (!isascii(c) || !isalnum(c) || strchr("$-_.+!*'(),", c) != NULL) { \ + *dst++ = '%'; \ + *dst++ = xtoa(((unsigned int)c >> 4) & 0xf); \ + *dst++ = xtoa((unsigned int)c & 0xf); \ + } else { \ + SVIS(dst, c, flag, nextc, extra); \ + } \ +while (/*CONSTCOND*/0) + +/* + * This is SVIS, the central macro of vis. + * dst: Pointer to the destination buffer + * c: Character to encode + * flag: Flag word + * nextc: The character following 'c' + * extra: Pointer to the list of extra characters to be + * backslash-protected. + */ +#define SVIS(dst, c, flag, nextc, extra) \ +do { \ + int isextra; \ + isextra = strchr(extra, c) != NULL; \ + if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) || \ + ((flag & VIS_SAFE) && issafe(c)))) { \ + *dst++ = c; \ + break; \ + } \ + if (flag & VIS_CSTYLE) { \ + switch (c) { \ + case '\n': \ + *dst++ = '\\'; *dst++ = 'n'; \ + continue; \ + case '\r': \ + *dst++ = '\\'; *dst++ = 'r'; \ + continue; \ + case '\b': \ + *dst++ = '\\'; *dst++ = 'b'; \ + continue; \ + case BELL: \ + *dst++ = '\\'; *dst++ = 'a'; \ + continue; \ + case '\v': \ + *dst++ = '\\'; *dst++ = 'v'; \ + continue; \ + case '\t': \ + *dst++ = '\\'; *dst++ = 't'; \ + continue; \ + case '\f': \ + *dst++ = '\\'; *dst++ = 'f'; \ + continue; \ + case ' ': \ + *dst++ = '\\'; *dst++ = 's'; \ + continue; \ + case '\0': \ + *dst++ = '\\'; *dst++ = '0'; \ + if (isoctal(nextc)) { \ + *dst++ = '0'; \ + *dst++ = '0'; \ + } \ + continue; \ + default: \ + if (isgraph(c)) { \ + *dst++ = '\\'; *dst++ = c; \ + continue; \ + } \ + } \ + } \ + if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) { \ + *dst++ = '\\'; \ + *dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + '0'; \ + *dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + '0'; \ + *dst++ = (c & 07) + '0'; \ + } else { \ + if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\'; \ + if (c & 0200) { \ + c &= 0177; *dst++ = 'M'; \ + } \ + if (iscntrl(c)) { \ + *dst++ = '^'; \ + if (c == 0177) \ + *dst++ = '?'; \ + else \ + *dst++ = c + '@'; \ + } else { \ + *dst++ = '-'; *dst++ = c; \ + } \ + } \ +} while (/*CONSTCOND*/0) + + +/* + * svis - visually encode characters, also encoding the characters + * pointed to by `extra' + */ +char * +svis(char *dst, int c, int flag, int nextc, const char *extra) +{ + char *nextra = NULL; + + _DIAGASSERT(dst != NULL); + _DIAGASSERT(extra != NULL); + MAKEEXTRALIST(flag, nextra, extra); + if (!nextra) { + *dst = '\0'; /* can't create nextra, return "" */ + return dst; + } + if (flag & VIS_HTTPSTYLE) + HVIS(dst, c, flag, nextc, nextra); + else + SVIS(dst, c, flag, nextc, nextra); + free(nextra); + *dst = '\0'; + return dst; +} + + +/* + * strsvis, strsvisx - visually encode characters from src into dst + * + * Extra is a pointer to a \0-terminated list of characters to + * be encoded, too. These functions are useful e. g. to + * encode strings in such a way so that they are not interpreted + * by a shell. + * + * Dst must be 4 times the size of src to account for possible + * expansion. The length of dst, not including the trailing NULL, + * is returned. + * + * Strsvisx encodes exactly len bytes from src into dst. + * This is useful for encoding a block of data. + */ +int +strsvis(char *dst, const char *csrc, int flag, const char *extra) +{ + int c; + char *start; + char *nextra = NULL; + const unsigned char *src = (const unsigned char *)csrc; + + _DIAGASSERT(dst != NULL); + _DIAGASSERT(src != NULL); + _DIAGASSERT(extra != NULL); + MAKEEXTRALIST(flag, nextra, extra); + if (!nextra) { + *dst = '\0'; /* can't create nextra, return "" */ + return 0; + } + if (flag & VIS_HTTPSTYLE) { + for (start = dst; (c = *src++) != '\0'; /* empty */) + HVIS(dst, c, flag, *src, nextra); + } else { + for (start = dst; (c = *src++) != '\0'; /* empty */) + SVIS(dst, c, flag, *src, nextra); + } + free(nextra); + *dst = '\0'; + return (dst - start); +} + + +int +strsvisx(char *dst, const char *csrc, size_t len, int flag, const char *extra) +{ + unsigned char c; + char *start; + char *nextra = NULL; + const unsigned char *src = (const unsigned char *)csrc; + + _DIAGASSERT(dst != NULL); + _DIAGASSERT(src != NULL); + _DIAGASSERT(extra != NULL); + MAKEEXTRALIST(flag, nextra, extra); + if (! nextra) { + *dst = '\0'; /* can't create nextra, return "" */ + return 0; + } + + if (flag & VIS_HTTPSTYLE) { + for (start = dst; len > 0; len--) { + c = *src++; + HVIS(dst, c, flag, len ? *src : '\0', nextra); + } + } else { + for (start = dst; len > 0; len--) { + c = *src++; + SVIS(dst, c, flag, len ? *src : '\0', nextra); + } + } + free(nextra); + *dst = '\0'; + return (dst - start); +} +#endif + +#if !HAVE_VIS +/* + * vis - visually encode characters + */ +char * +vis(char *dst, int c, int flag, int nextc) +{ + char *extra = NULL; + unsigned char uc = (unsigned char)c; + + _DIAGASSERT(dst != NULL); + + MAKEEXTRALIST(flag, extra, ""); + if (! extra) { + *dst = '\0'; /* can't create extra, return "" */ + return dst; + } + if (flag & VIS_HTTPSTYLE) + HVIS(dst, uc, flag, nextc, extra); + else + SVIS(dst, uc, flag, nextc, extra); + free(extra); + *dst = '\0'; + return dst; +} + + +/* + * strvis, strvisx - visually encode characters from src into dst + * + * Dst must be 4 times the size of src to account for possible + * expansion. The length of dst, not including the trailing NULL, + * is returned. + * + * Strvisx encodes exactly len bytes from src into dst. + * This is useful for encoding a block of data. + */ +int +strvis(char *dst, const char *src, int flag) +{ + char *extra = NULL; + int rv; + + MAKEEXTRALIST(flag, extra, ""); + if (!extra) { + *dst = '\0'; /* can't create extra, return "" */ + return 0; + } + rv = strsvis(dst, src, flag, extra); + free(extra); + return rv; +} + + +int +strvisx(char *dst, const char *src, size_t len, int flag) +{ + char *extra = NULL; + int rv; + + MAKEEXTRALIST(flag, extra, ""); + if (!extra) { + *dst = '\0'; /* can't create extra, return "" */ + return 0; + } + rv = strsvisx(dst, src, len, flag, extra); + free(extra); + return rv; +} +#endif diff --git a/cpukit/libmisc/shell/vis.h b/cpukit/libmisc/shell/vis.h new file mode 100644 index 0000000000..fb0aa6dc2a --- /dev/null +++ b/cpukit/libmisc/shell/vis.h @@ -0,0 +1,91 @@ +/* $NetBSD: vis.h,v 1.16 2005/09/13 01:44:32 christos Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + * + * @(#)vis.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _VIS_H_ +#define _VIS_H_ + +#include <sys/types.h> + +/* + * to select alternate encoding format + */ +#define VIS_OCTAL 0x01 /* use octal \ddd format */ +#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropiate */ + +/* + * to alter set of characters encoded (default is to encode all + * non-graphic except space, tab, and newline). + */ +#define VIS_SP 0x04 /* also encode space */ +#define VIS_TAB 0x08 /* also encode tab */ +#define VIS_NL 0x10 /* also encode newline */ +#define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) +#define VIS_SAFE 0x20 /* only encode "unsafe" characters */ + +/* + * other + */ +#define VIS_NOSLASH 0x40 /* inhibit printing '\' */ +#define VIS_HTTPSTYLE 0x80 /* http-style escape % HEX HEX */ + +/* + * unvis return codes + */ +#define UNVIS_VALID 1 /* character valid */ +#define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */ +#define UNVIS_NOCHAR 3 /* valid sequence, no character produced */ +#define UNVIS_SYNBAD -1 /* unrecognized escape sequence */ +#define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */ + +/* + * unvis flags + */ +#define UNVIS_END 1 /* no more characters */ + +#include <sys/cdefs.h> + +__BEGIN_DECLS +char *vis(char *, int, int, int); +char *svis(char *, int, int, int, const char *); +int strvis(char *, const char *, int); +int strsvis(char *, const char *, int, const char *); +int strvisx(char *, const char *, size_t, int); +int strsvisx(char *, const char *, size_t, int, const char *); +int strunvis(char *, const char *); +int strunvisx(char *, const char *, int); +#ifndef __LIBC12_SOURCE__ +//int unvis(char *, int, int *, int) __RENAME(__unvis13); +#endif +__END_DECLS + +#endif /* !_VIS_H_ */ diff --git a/cpukit/libmisc/shell/vwarn.c b/cpukit/libmisc/shell/vwarn.c new file mode 100644 index 0000000000..df214c5db9 --- /dev/null +++ b/cpukit/libmisc/shell/vwarn.c @@ -0,0 +1,74 @@ +/* $NetBSD: vwarn.c,v 1.13 2005/09/13 01:44:09 christos Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)err.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: vwarn.c,v 1.13 2005/09/13 01:44:09 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include <err.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef __weak_alias +__weak_alias(vwarn, _vwarn) +#endif + +void +vwarn(const char *fmt, _BSD_VA_LIST_ ap) +{ + int sverrno; + + sverrno = errno; +#if 0 + (void)fprintf(stderr, "%s: ", getprogname()); +#endif + if (fmt != NULL) { + (void)vfprintf(stdout, fmt, ap); + (void)fprintf(stdout, ": "); + } + (void)fprintf(stdout, "%s\n", strerror(sverrno)); +} diff --git a/cpukit/libmisc/shell/vwarnx.c b/cpukit/libmisc/shell/vwarnx.c new file mode 100644 index 0000000000..34737a17f1 --- /dev/null +++ b/cpukit/libmisc/shell/vwarnx.c @@ -0,0 +1,67 @@ +/* $NetBSD: vwarnx.c,v 1.13 2005/09/13 01:44:09 christos Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)err.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: vwarnx.c,v 1.13 2005/09/13 01:44:09 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include <err.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#ifdef __weak_alias +__weak_alias(vwarnx, _vwarnx) +#endif + +void +vwarnx(const char *fmt, _BSD_VA_LIST_ ap) +{ +#if 0 + (void)fprintf(stderr, "%s: ", getprogname()); +#endif + if (fmt != NULL) + (void)vfprintf(stdout, fmt, ap); + (void)fprintf(stdout, "\n"); +} diff --git a/cpukit/libmisc/shell/warn.c b/cpukit/libmisc/shell/warn.c new file mode 100644 index 0000000000..f32edfb630 --- /dev/null +++ b/cpukit/libmisc/shell/warn.c @@ -0,0 +1,64 @@ +/* $NetBSD: warn.c,v 1.13 2005/09/13 13:51:50 christos Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)err.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: warn.c,v 1.13 2005/09/13 13:51:50 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include <err.h> +#include <stdarg.h> + +#ifdef __weak_alias +__weak_alias(warn, _warn) +#endif + +void +warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarn(fmt, ap); + va_end(ap); +} diff --git a/cpukit/libmisc/shell/warnx.c b/cpukit/libmisc/shell/warnx.c new file mode 100644 index 0000000000..efcbd8b0f1 --- /dev/null +++ b/cpukit/libmisc/shell/warnx.c @@ -0,0 +1,64 @@ +/* $NetBSD: warnx.c,v 1.13 2005/09/13 13:51:50 christos Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)err.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: warnx.c,v 1.13 2005/09/13 13:51:50 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include <err.h> +#include <stdarg.h> + +#ifdef __weak_alias +__weak_alias(warnx, _warnx) +#endif + +void +warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); +} diff --git a/cpukit/libmisc/shell/write_file.c b/cpukit/libmisc/shell/write_file.c new file mode 100644 index 0000000000..f9e72f4645 --- /dev/null +++ b/cpukit/libmisc/shell/write_file.c @@ -0,0 +1,43 @@ +/* + * + * Write buffer to a file + * + * Author: + * + * WORK: fernando.ruiz@ctv.es + * HOME: correo@fernando-ruiz.com + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> + +void rtems_shell_write_file( + const char *name, + const char *content +) +{ + FILE * fd; + + fd = fopen(name,"w"); + if ( !fd ) { + fprintf( stderr, "Unable to write %s\n", name ); + } + + if (fd) { + fwrite(content,1,strlen(content),fd); + fclose(fd); + } +} + + diff --git a/cpukit/libmisc/stackchk/README b/cpukit/libmisc/stackchk/README new file mode 100644 index 0000000000..5421a77434 --- /dev/null +++ b/cpukit/libmisc/stackchk/README @@ -0,0 +1,53 @@ +# +# $Id$ +# + +Introduction +============ + +This directory contains a stack bounds checker. It provides two +primary features: + + + check for stack overflow at each context switch + + provides an educated guess at each task's stack usage + +Enabling +======== + +Add the stack checker extension to the initial user extension set. +If using confdefs.h to build your configuration table, this is +as simple as adding -DSTACK_CHECK_ON to the gcc command line which +compiles the file defining the configuration table. In the RTEMS +test suites and samples, this is always init.c + +Background +========== + +The stack overflow check at context switch works by looking for +a 16 byte pattern at the logical end of the stack to be corrupted. +The "guesser" assumes that the entire stack was prefilled with a known +pattern and assumes that the pattern is still in place if the memory +has not been used as a stack. + +Both of these can be fooled by pushing large holes onto the stack +and not writing to them... or (much more unlikely) writing the +magic patterns into memory. + +This code has not been extensively tested. It is provided as a tool +for RTEMS users to catch the most common mistake in multitasking +systems ... too little stack space. Suggestions and comments are appreciated. + +NOTES: + +1. Stack usage information is questionable on CPUs which push + large holes on stack. + +2. The stack checker has a tendency to generate a fault when + trying to print the helpful diagnostic message. If it comes + out, congratulations. If not, then the variable Stack_check_Blown_task + contains a pointer to the TCB of the offending task. This + is usually enough to go on. + +FUTURE: + +1. Determine how/if gcc will generate stack probe calls and support that. diff --git a/cpukit/libmisc/stackchk/check.c b/cpukit/libmisc/stackchk/check.c new file mode 100644 index 0000000000..d2c0b1001b --- /dev/null +++ b/cpukit/libmisc/stackchk/check.c @@ -0,0 +1,515 @@ +/* + * Stack Overflow Check User Extension Set + * + * NOTE: This extension set automatically determines at + * initialization time whether the stack for this + * CPU grows up or down and installs the correct + * extension routines for that direction. + * + * COPYRIGHT (c) 1989-2010. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <inttypes.h> + +/* + * The stack dump information may be printed by a "fatal" extension. + * Fatal extensions only get called via rtems_fatal_error_occurred() + * and not when rtems_shutdown_executive() is called. + * When that happens, this #define should be deleted and all the code + * it marks. + */ +#define DONT_USE_FATAL_EXTENSION + +#include <string.h> +#include <stdlib.h> + +#include <rtems/bspIo.h> +#include <rtems/stackchk.h> +#include "internal.h" + +/* + * Variable to indicate when the stack checker has been initialized. + */ +static int Stack_check_Initialized = 0; + +/* + * The "magic pattern" used to mark the end of the stack. + */ +Stack_check_Control Stack_check_Pattern; + +/* + * Helper function to report if the actual stack pointer is in range. + * + * NOTE: This uses a GCC specific method. + */ +static inline bool Stack_check_Frame_pointer_in_range( + Stack_Control *the_stack +) +{ + #if defined(__GNUC__) + void *sp = __builtin_frame_address(0); + + if ( sp < the_stack->area ) { + return false; + } + if ( sp > (the_stack->area + the_stack->size) ) { + return false; + } + #else + #error "How do I check stack bounds on a non-GNU compiler?" + #endif + return true; +} + +/* + * Where the pattern goes in the stack area is dependent upon + * whether the stack grow to the high or low area of the memory. + */ +#if (CPU_STACK_GROWS_UP == TRUE) + #define Stack_check_Get_pattern( _the_stack ) \ + ((char *)(_the_stack)->area + \ + (_the_stack)->size - sizeof( Stack_check_Control ) ) + + #define Stack_check_Calculate_used( _low, _size, _high_water ) \ + ((char *)(_high_water) - (char *)(_low)) + + #define Stack_check_usable_stack_start(_the_stack) \ + ((_the_stack)->area) + +#else + /* + * We need this magic offset because during a task delete the task stack will + * be freed before we enter the task switch extension which checks the stack. + * The task stack free operation will write the next and previous pointers + * for the free list into this area. + */ + #define Stack_check_Get_pattern( _the_stack ) \ + ((char *)(_the_stack)->area + sizeof(Heap_Block) - HEAP_BLOCK_HEADER_SIZE) + + #define Stack_check_Calculate_used( _low, _size, _high_water) \ + ( ((char *)(_low) + (_size)) - (char *)(_high_water) ) + + #define Stack_check_usable_stack_start(_the_stack) \ + ((char *)(_the_stack)->area + sizeof(Stack_check_Control)) + +#endif + +/* + * Obtain a properly typed pointer to the area to check. + */ +#define Stack_check_Get_pattern_area( _the_stack ) \ + (Stack_check_Control *) Stack_check_Get_pattern( _the_stack ) + +/* + * The assumption is that if the pattern gets overwritten, the task + * is too close. This defines the usable stack memory. + */ +#define Stack_check_usable_stack_size(_the_stack) \ + ((_the_stack)->size - sizeof(Stack_check_Control)) + +#if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE) + /* + * Did RTEMS allocate the interrupt stack? If so, put it in + * Stack_Control format. + */ + Stack_Control Stack_check_Interrupt_stack; +#endif + +/* + * Fill an entire stack area with BYTE_PATTERN. This will be used + * to check for amount of actual stack used. + */ +#define Stack_check_Dope_stack(_stack) \ + memset((_stack)->area, BYTE_PATTERN, (_stack)->size) + +/* + * Stack_check_Initialize + */ +void Stack_check_Initialize( void ) +{ + int i; + uint32_t *p; + static uint32_t pattern[ 4 ] = { + 0xFEEDF00D, 0x0BAD0D06, /* FEED FOOD to BAD DOG */ + 0xDEADF00D, 0x600D0D06 /* DEAD FOOD but GOOD DOG */ + }; + + if ( Stack_check_Initialized ) + return; + + /* + * Dope the pattern and fill areas + */ + p = Stack_check_Pattern.pattern; + for ( i = 0; i < PATTERN_SIZE_WORDS; i++ ) { + p[i] = pattern[ i%4 ]; + } + + /* + * If appropriate, setup the interrupt stack for high water testing + * also. + */ + #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE) + if (_CPU_Interrupt_stack_low && _CPU_Interrupt_stack_high) { + Stack_check_Interrupt_stack.area = _CPU_Interrupt_stack_low; + Stack_check_Interrupt_stack.size = (char *) _CPU_Interrupt_stack_high - + (char *) _CPU_Interrupt_stack_low; + Stack_check_Dope_stack(&Stack_check_Interrupt_stack); + } + #endif + + Stack_check_Initialized = 1; +} + +/* + * rtems_stack_checker_create_extension + */ +bool rtems_stack_checker_create_extension( + Thread_Control *running __attribute__((unused)), + Thread_Control *the_thread +) +{ + Stack_check_Initialize(); + + if (the_thread) + Stack_check_Dope_stack(&the_thread->Start.Initial_stack); + + return true; +} + +/* + * rtems_stack_checker_Begin_extension + */ +void rtems_stack_checker_begin_extension( + Thread_Control *the_thread +) +{ + Stack_check_Control *the_pattern; + + if ( the_thread->Object.id == 0 ) /* skip system tasks */ + return; + + the_pattern = Stack_check_Get_pattern_area(&the_thread->Start.Initial_stack); + + *the_pattern = Stack_check_Pattern; +} + +/* + * Stack_check_report_blown_task + * + * Report a blown stack. Needs to be a separate routine + * so that interrupt handlers can use this too. + * + * NOTE: The system is in a questionable state... we may not get + * the following message out. + */ +void Stack_check_report_blown_task( + Thread_Control *running, + bool pattern_ok +) RTEMS_COMPILER_NO_RETURN_ATTRIBUTE; + +void Stack_check_report_blown_task(Thread_Control *running, bool pattern_ok) +{ + Stack_Control *stack = &running->Start.Initial_stack; + void *pattern_area = Stack_check_Get_pattern(stack); + char name[32]; + + printk("BLOWN STACK!!!\n"); + printk("task control block: 0x%08" PRIxPTR "\n", running); + printk("task ID: 0x%08lx\n", (unsigned long) running->Object.id); + printk( + "task name: 0x%08" PRIx32 "\n", + running->Object.name.name_u32 + ); + printk( + "task name string: %s\n", + rtems_object_get_name(running->Object.id, sizeof(name), name) + ); + printk( + "task stack area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n", + (unsigned long) stack->size, + stack->area, + ((char *) stack->area + stack->size) + ); + if (!pattern_ok) { + printk( + "damaged pattern area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n", + (unsigned long) PATTERN_SIZE_BYTES, + pattern_area, + (pattern_area + PATTERN_SIZE_BYTES) + ); + } + + #if defined(RTEMS_MULTIPROCESSING) + if (rtems_configuration_get_user_multiprocessing_table()) { + printk( + "node: 0x%08" PRIxPTR "\n", + rtems_configuration_get_user_multiprocessing_table()->node + ); + } + #endif + + rtems_fatal_error_occurred(0x81); +} + +/* + * rtems_stack_checker_switch_extension + */ +void rtems_stack_checker_switch_extension( + Thread_Control *running __attribute__((unused)), + Thread_Control *heir __attribute__((unused)) +) +{ + Stack_Control *the_stack = &running->Start.Initial_stack; + void *pattern; + bool sp_ok; + bool pattern_ok = true; + + pattern = Stack_check_Get_pattern_area(the_stack); + + /* + * Check for an out of bounds stack pointer or an overwrite + */ + sp_ok = Stack_check_Frame_pointer_in_range( the_stack ); + + pattern_ok = (!memcmp( pattern, + (void *) Stack_check_Pattern.pattern, PATTERN_SIZE_BYTES)); + + if ( !sp_ok || !pattern_ok ) { + Stack_check_report_blown_task( running, pattern_ok ); + } +} + +/* + * Check if blown + */ +bool rtems_stack_checker_is_blown( void ) +{ + Stack_Control *the_stack = &_Thread_Executing->Start.Initial_stack; + bool sp_ok; + bool pattern_ok = true; + + /* + * Check for an out of bounds stack pointer + */ + + sp_ok = Stack_check_Frame_pointer_in_range( the_stack ); + + /* + * The stack checker must be initialized before the pattern is there + * to check. + */ + if ( Stack_check_Initialized ) { + pattern_ok = (!memcmp( + Stack_check_Get_pattern(the_stack), + (void *) Stack_check_Pattern.pattern, + PATTERN_SIZE_BYTES + )); + } + + + /* + * Let's report as much as we can. + */ + if ( !sp_ok || !pattern_ok ) { + Stack_check_report_blown_task( _Thread_Executing, pattern_ok ); + /* DOES NOT RETURN */ + } + + /* + * The Stack Pointer and the Pattern Area are OK so return false. + */ + return false; +} + +/* + * Stack_check_find_high_water_mark + */ +static inline void *Stack_check_find_high_water_mark( + const void *s, + size_t n +) +{ + const uint32_t *base, *ebase; + uint32_t length; + + base = s; + length = n/4; + + #if ( CPU_STACK_GROWS_UP == TRUE ) + /* + * start at higher memory and find first word that does not + * match pattern + */ + + base += length - 1; + for (ebase = s; base > ebase; base--) + if (*base != U32_PATTERN) + return (void *) base; + #else + /* + * start at lower memory and find first word that does not + * match pattern + */ + + base += PATTERN_SIZE_WORDS; + for (ebase = base + length; base < ebase; base++) + if (*base != U32_PATTERN) + return (void *) base; + #endif + + return (void *)0; +} + +/* + * Stack_check_Dump_threads_usage + * + * Try to print out how much stack was actually used by the task. + */ +static void *print_context; +static rtems_printk_plugin_t print_handler; + +void Stack_check_Dump_threads_usage( + Thread_Control *the_thread +) +{ + uint32_t size, used; + void *low; + void *high_water_mark; + void *current; + Stack_Control *stack; + char name[5]; + + /* + * The pointer passed in for the_thread is guaranteed to be non-NULL from + * rtems_iterate_over_all_threads() so no need to check it here. + */ + + /* + * Obtain interrupt stack information + */ + #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE) + if (the_thread == (Thread_Control *) -1) { + if (!Stack_check_Interrupt_stack.area) + return; + stack = &Stack_check_Interrupt_stack; + the_thread = 0; + current = 0; + } else + #endif + { + stack = &the_thread->Start.Initial_stack; + current = (void *)_CPU_Context_Get_SP( &the_thread->Registers ); + } + + low = Stack_check_usable_stack_start(stack); + size = Stack_check_usable_stack_size(stack); + + high_water_mark = Stack_check_find_high_water_mark(low, size); + + if ( high_water_mark ) + used = Stack_check_Calculate_used( low, size, high_water_mark ); + else + used = 0; + + + #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE) + if ( the_thread ) + #endif + { + (*print_handler)( + print_context, + "0x%08" PRIx32 " %4s", + the_thread->Object.id, + rtems_object_get_name( the_thread->Object.id, sizeof(name), name ) + ); + } + #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE) + else { + (*print_handler)( print_context, "0x%08" PRIx32 " INTR", ~0 ); + } + #endif + + (*print_handler)( + print_context, + " %010p - %010p %010p %8" PRId32 " ", + stack->area, + stack->area + stack->size - 1, + current, + size + ); + + if (Stack_check_Initialized == 0) { + (*print_handler)( print_context, "Unavailable\n" ); + } else { + (*print_handler)( print_context, "%8" PRId32 "\n", used ); + } + + +} + +/* + * rtems_stack_checker_fatal_extension + */ +#ifndef DONT_USE_FATAL_EXTENSION + void rtems_stack_checker_fatal_extension( + Internal_errors_Source source, + bool is_internal, + uint32_t status + ) + { + if (status == 0) + rtems_stack_checker_report_usage(); + } +#endif + +/*PAGE + * + * rtems_stack_checker_report_usage + */ + +void rtems_stack_checker_report_usage_with_plugin( + void *context, + rtems_printk_plugin_t print +) +{ + if ( !print ) + return; + + print_context = context; + print_handler = print; + + (*print)( context, "Stack usage by thread\n"); + (*print)( context, +" ID NAME LOW HIGH CURRENT AVAILABLE USED\n" + ); + + /* iterate over all threads and dump the usage */ + rtems_iterate_over_all_threads( Stack_check_Dump_threads_usage ); + + #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE) + /* dump interrupt stack info if any */ + Stack_check_Dump_threads_usage((Thread_Control *) -1); + #endif + + print_context = NULL; + print_handler = NULL; +} + +void rtems_stack_checker_report_usage( void ) +{ + rtems_stack_checker_report_usage_with_plugin( NULL, printk_plugin ); +} diff --git a/cpukit/libmisc/stackchk/internal.h b/cpukit/libmisc/stackchk/internal.h new file mode 100644 index 0000000000..a16a61e30e --- /dev/null +++ b/cpukit/libmisc/stackchk/internal.h @@ -0,0 +1,95 @@ +/* internal.h + * + * This include file contains internal information + * for the RTEMS stack checker. + * + * COPYRIGHT (c) 1989-2006. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef __INTERNAL_STACK_CHECK_h +#define __INTERNAL_STACK_CHECK_h + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This structure is used to fill in and compare the "end of stack" + * marker pattern. + * pattern area must be a multiple of 4 words. + */ + +#ifdef CPU_STACK_CHECK_SIZE +#define PATTERN_SIZE_WORDS (((CPU_STACK_CHECK_SIZE / 4) + 3) & ~0x3) +#else +#define PATTERN_SIZE_WORDS (4) +#endif + +#define PATTERN_SIZE_BYTES (PATTERN_SIZE_WORDS * sizeof(uint32_t)) + +typedef struct { + uint32_t pattern[ PATTERN_SIZE_WORDS ]; +} Stack_check_Control; + +/* + * The pattern used to fill the entire stack. + */ + +#define BYTE_PATTERN 0xA5 +#define U32_PATTERN 0xA5A5A5A5 + +/* + * rtems_stack_checker_create_extension + */ + +bool rtems_stack_checker_create_extension( + Thread_Control *running, + Thread_Control *the_thread +); + +/* + * rtems_stack_checker_begin_extension + */ + +void rtems_stack_checker_begin_extension( + Thread_Control *the_thread +); + +/* + * rtems_stack_checker_switch_extension + */ + +void rtems_stack_checker_switch_extension( + Thread_Control *running, + Thread_Control *heir +); + +/* + * rtems_stack_checker_fatal_extension + */ + +void rtems_stack_checker_fatal_extension( + Internal_errors_Source source, + bool is_internal, + uint32_t status +); + +/* + * rtems_stack_checker_report_usage + */ + +void rtems_stack_checker_report_usage( void ); + +#ifdef __cplusplus +} +#endif + +#endif +/* end of include file */ diff --git a/cpukit/libmisc/stackchk/stackchk.h b/cpukit/libmisc/stackchk/stackchk.h new file mode 100644 index 0000000000..02bbac3a45 --- /dev/null +++ b/cpukit/libmisc/stackchk/stackchk.h @@ -0,0 +1,135 @@ +/** @file rtems/stackchk.h + * + * This include file contains information necessary to utilize + * and install the stack checker mechanism. + */ + +/* + * COPYRIGHT (c) 1989-2007. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef _RTEMS_STACKCHK_H +#define _RTEMS_STACKCHK_H + +#include <stdbool.h> /* bool */ + +#include <rtems/score/percpu.h> /* Thread_Control */ +#include <rtems/bspIo.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Has Current Task Blown Its Stack + * + * This method is used to determine if the current stack pointer + * of the currently executing task is within bounds. + * + * @return This method returns true if the currently executing task + * has blown its stack. + * + */ +bool rtems_stack_checker_is_blown( void ); + +/** @brief Print Stack Usage Report + * + * This method prints a stack usage report for the curently executing + * task. + * + * @note It uses printk to print the report. + */ +void rtems_stack_checker_report_usage( void ); + +/** @brief Print Stack Usage Report + * + * This method prints a stack usage report for the curently executing + * task. + * + * @param[in] context is the context to pass to the print handler + * @param[in] print is the print handler + * + * @note It uses the caller's routine to print the report. + */ +void rtems_stack_checker_report_usage_with_plugin( + void *context, + rtems_printk_plugin_t print +); + +/************************************************************* + ************************************************************* + ** Prototyped only so the user extension can be installed ** + ************************************************************* + *************************************************************/ + +/** @brief Stack Checker Task Create Extension + * + * This method is the task create extension for the stack checker. + * + * @param[in] running points to the currently executing task + * @param[in] the_thread points to the newly created task + * + * @note If this this the first task created, the stack checker + * will automatically intialize itself. + */ +bool rtems_stack_checker_create_extension( + Thread_Control *running, + Thread_Control *the_thread +); + +/** @brief Stack Checker Task Begin Extension + * + * This method is the task begin extension for the stack checker. + * + * @param[in] the_thread points to task starting to execute + * + * @note This is called from the internal method _Thread_Handler. + */ +void rtems_stack_checker_begin_extension( + Thread_Control *the_thread +); + +/** @brief Stack Checker Task Context Switch Extension + * + * This method is the task context switch extension for the stack checker. + * + * @param[in] running points to the currently executing task which + * is being context switched out + * @param[in] running points to the heir task which we are switching to + * + * @note This is called from the internal method _Thread_Dispatch. + */ +void rtems_stack_checker_switch_extension( + Thread_Control *running, + Thread_Control *heir +); + +/** @brief Stack Checker Extension Set Definition + * + * This macro defines the user extension handler set for the stack + * checker. This macro is normally only used by confdefs.h. + */ +#define RTEMS_STACK_CHECKER_EXTENSION \ +{ \ + rtems_stack_checker_create_extension, /* rtems_task_create */ \ + 0, /* rtems_task_start */ \ + 0, /* rtems_task_restart */ \ + 0, /* rtems_task_delete */ \ + rtems_stack_checker_switch_extension, /* task_switch */ \ + rtems_stack_checker_begin_extension, /* task_begin */ \ + 0, /* task_exitted */ \ + 0 /* rtems_stack_checker_fatal_extension */, /* fatal */ \ +} + +#ifdef __cplusplus +} +#endif + +#endif +/* end of include file */ diff --git a/cpukit/libmisc/stringto/stringto.h b/cpukit/libmisc/stringto/stringto.h new file mode 100644 index 0000000000..dfb1617bc9 --- /dev/null +++ b/cpukit/libmisc/stringto/stringto.h @@ -0,0 +1,246 @@ +/* + * COPYRIGHT (c) 2009. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef _RTEMS_STRINGTO_H +#define _RTEMS_STRINGTO_H + +#include <rtems.h> + +/** + * @brief Convert String to Pointer (with validation) + * + * This method converts a string to a pointer (void *) with + * basic numeric validation. + * + * @param[in] s is the string to convert + * @param[in] n points to the variable to place the converted output in + * @param[in] endptr is used to keep track of the position in the string + * + * @return This method returns RTEMS_SUCCESSFUL on successful conversion + * and *n is filled in. Otherwise, the status indicates the + * source of the error. + */ +rtems_status_code rtems_string_to_pointer( + const char *s, + void **n, + char **endptr +); + +/** + * @brief Convert String to Unsigned Character (with validation) + * + * This method converts a string to an unsigned character with + * range validation. + * + * @param[in] s is the string to convert + * @param[in] n points to the variable to place the converted output in + * @param[in] endptr is used to keep track of the position in the string + * @param[in] base is the expected base of the number + * + * @return This method returns RTEMS_SUCCESSFUL on successful conversion + * and *n is filled in. Otherwise, the status indicates the + * source of the error. + */ +rtems_status_code rtems_string_to_unsigned_char( + const char *s, + unsigned char *n, + char **endptr, + int base +); + +/** + * @brief Convert String to Int (with validation) + * + * This method converts a string to an int with range validation. + * + * @param[in] s is the string to convert + * @param[in] n points to the variable to place the converted output in + * @param[in] endptr is used to keep track of the position in the string + * @param[in] base is the expected base of the number + * + * @return This method returns RTEMS_SUCCESSFUL on successful conversion + * and *n is filled in. Otherwise, the status indicates the + * source of the error. + */ +rtems_status_code rtems_string_to_int( + const char *s, + int *n, + char **endptr, + int base +); + +/** + * @brief Convert String to Unsigned Int (with validation) + * + * This method converts a string to an unsigned int with range validation. + * + * @param[in] s is the string to convert + * @param[in] n points to the variable to place the converted output in + * @param[in] endptr is used to keep track of the position in the string + * @param[in] base is the expected base of the number + * + * @return This method returns RTEMS_SUCCESSFUL on successful conversion + * and *n is filled in. Otherwise, the status indicates the + * source of the error. + */ +rtems_status_code rtems_string_to_unsigned_int( + const char *s, + unsigned int *n, + char **endptr, + int base +); + +/** + * @brief Convert String to Long (with validation) + * + * This method converts a string to a long with + * range validation. + * + * @param[in] s is the string to convert + * @param[in] n points to the variable to place the converted output in + * @param[in] endptr is used to keep track of the position in the string + * @param[in] base is the expected base of the number + * + * @return This method returns RTEMS_SUCCESSFUL on successful conversion + * and *n is filled in. Otherwise, the status indicates the + * source of the error. + */ +rtems_status_code rtems_string_to_long( + const char *s, + long *n, + char **endptr, + int base +); + +/** + * @brief Convert String to Unsigned Long (with validation) + * + * This method converts a string to an unsigned long with + * range validation. + * + * @param[in] s is the string to convert + * @param[in] n points to the variable to place the converted output in + * @param[in] endptr is used to keep track of the position in the string + * @param[in] base is the expected base of the number + * + * @return This method returns RTEMS_SUCCESSFUL on successful conversion + * and *n is filled in. Otherwise, the status indicates the + * source of the error. + */ +rtems_status_code rtems_string_to_unsigned_long( + const char *s, + unsigned long *n, + char **endptr, + int base +); + +/** + * @brief Convert String to Long Long (with validation) + * + * This method converts a string to a long long with + * range validation. + * + * @param[in] s is the string to convert + * @param[in] n points to the variable to place the converted output in + * @param[in] endptr is used to keep track of the position in the string + * @param[in] base is the expected base of the number + * + * @return This method returns RTEMS_SUCCESSFUL on successful conversion + * and *n is filled in. Otherwise, the status indicates the + * source of the error. + */ +rtems_status_code rtems_string_to_long_long( + const char *s, + long long *n, + char **endptr, + int base +); + +/** + * @brief Convert String to Unsigned Long Long (with validation) + * + * This method converts a string to an unsigned character with + * range validation. + * + * @param[in] s is the string to convert + * @param[in] n points to the variable to place the converted output in + * @param[in] endptr is used to keep track of the position in the string + * @param[in] base is the expected base of the number + * + * @return This method returns RTEMS_SUCCESSFUL on successful conversion + * and *n is filled in. Otherwise, the status indicates the + * source of the error. + */ +rtems_status_code rtems_string_to_unsigned_long_long( + const char *s, + unsigned long long *n, + char **endptr, + int base +); + +/** + * @brief Convert String to Float (with validation) + * + * This method converts a string to a float with range validation. + * + * @param[in] s is the string to convert + * @param[in] n points to the variable to place the converted output in + * @param[in] endptr is used to keep track of the position in the string + * + * @return This method returns RTEMS_SUCCESSFUL on successful conversion + * and *n is filled in. Otherwise, the status indicates the + * source of the error. + */ +rtems_status_code rtems_string_to_float( + const char *s, + float *n, + char **endptr +); + +/** + * @brief Convert String to Double (with validation) + * + * This method converts a string to a double with range validation. + * + * @param[in] s is the string to convert + * @param[in] n points to the variable to place the converted output in + * @param[in] endptr is used to keep track of the position in the string + * + * @return This method returns RTEMS_SUCCESSFUL on successful conversion + * and *n is filled in. Otherwise, the status indicates the + * source of the error. + */ +rtems_status_code rtems_string_to_double( + const char *s, + double *n, + char **endptr +); + +/** + * @brief Convert String to long double (with validation) + * + * This method converts a string to a long double with range validation. + * + * @param[in] s is the string to convert + * @param[in] n points to the variable to place the converted output in + * @param[in] endptr is used to keep track of the position in the string + * + * @return This method returns RTEMS_SUCCESSFUL on successful conversion + * and *n is filled in. Otherwise, the status indicates the + * source of the error. + */ +rtems_status_code rtems_string_to_long_double( + const char *s, + long double *n, + char **endptr +); + +#endif diff --git a/cpukit/libmisc/stringto/stringtodouble.c b/cpukit/libmisc/stringto/stringtodouble.c new file mode 100644 index 0000000000..ec50a1332b --- /dev/null +++ b/cpukit/libmisc/stringto/stringtodouble.c @@ -0,0 +1,58 @@ +/* + * COPYRIGHT (c) 2009. + * On-Line Applications Research Corporation (OAR). + * + * Copyright (c) 2011 Ralf Corsépius, Ulm, Germany. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <math.h> + +#include <rtems/stringto.h> + +/* + * Instantiate an error checking wrapper for strtod (double) + */ + +rtems_status_code rtems_string_to_double ( + const char *s, + double *n, + char **endptr +) +{ + double result; + char *end; + + if ( !n ) + return RTEMS_INVALID_ADDRESS; + + errno = 0; + *n = 0; + + result = strtod( s, &end ); + + if ( endptr ) + *endptr = end; + + if ( end == s ) + return RTEMS_NOT_DEFINED; + + if ( ( errno == ERANGE ) && + (( result == 0 ) || ( result == HUGE_VAL ) || ( result == -HUGE_VAL ))) + return RTEMS_INVALID_NUMBER; + + *n = result; + + return RTEMS_SUCCESSFUL; +} diff --git a/cpukit/libmisc/stringto/stringtofloat.c b/cpukit/libmisc/stringto/stringtofloat.c new file mode 100644 index 0000000000..05ebe33566 --- /dev/null +++ b/cpukit/libmisc/stringto/stringtofloat.c @@ -0,0 +1,58 @@ +/* + * COPYRIGHT (c) 2009. + * On-Line Applications Research Corporation (OAR). + * + * Copyright (c) 2011 Ralf Corsépius, Ulm, Germany. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <math.h> + +#include <rtems/stringto.h> + +/* + * Instantiate an error checking wrapper for strtof (float) + */ + +rtems_status_code rtems_string_to_float ( + const char *s, + float *n, + char **endptr +) +{ + float result; + char *end; + + if ( !n ) + return RTEMS_INVALID_ADDRESS; + + errno = 0; + *n = 0; + + result = strtof( s, &end ); + + if ( endptr ) + *endptr = end; + + if ( end == s ) + return RTEMS_NOT_DEFINED; + + if ( ( errno == ERANGE ) && + (( result == 0 ) || ( result == HUGE_VALF ) || ( result == -HUGE_VALF ))) + return RTEMS_INVALID_NUMBER; + + *n = result; + + return RTEMS_SUCCESSFUL; +} diff --git a/cpukit/libmisc/stringto/stringtoint.c b/cpukit/libmisc/stringto/stringtoint.c new file mode 100644 index 0000000000..8e3663b496 --- /dev/null +++ b/cpukit/libmisc/stringto/stringtoint.c @@ -0,0 +1,73 @@ +/* + * COPYRIGHT (c) 2009. + * On-Line Applications Research Corporation (OAR). + * + * Copyright (c) 2011 Ralf Corsépius, Ulm, Germany. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <limits.h> + +#include <rtems/stringto.h> + +/* + * Instantiate an error checking wrapper for strtol (int) + */ + +rtems_status_code rtems_string_to_int ( + const char *s, + int *n, + char **endptr, + int base +) +{ + long result; + char *end; + + if ( !n ) + return RTEMS_INVALID_ADDRESS; + + errno = 0; + *n = 0; + + result = strtol( s, &end, base ); + + if ( endptr ) + *endptr = end; + + if ( end == s ) + return RTEMS_NOT_DEFINED; + + if ( ( errno == ERANGE ) && + (( result == 0 ) || ( result == LONG_MAX ) || ( result == LONG_MIN ))) + return RTEMS_INVALID_NUMBER; + +#if (INT_MAX < LONG_MAX) + if ( result > INT_MAX ) { + errno = ERANGE; + return RTEMS_INVALID_NUMBER; + } +#endif + +#if (INT_MIN < LONG_MIN) + if ( result < INT_MIN ) { + errno = ERANGE; + return RTEMS_INVALID_NUMBER; + } +#endif + + *n = result; + + return RTEMS_SUCCESSFUL; +} diff --git a/cpukit/libmisc/stringto/stringtolong.c b/cpukit/libmisc/stringto/stringtolong.c new file mode 100644 index 0000000000..e77b8822b8 --- /dev/null +++ b/cpukit/libmisc/stringto/stringtolong.c @@ -0,0 +1,59 @@ +/* + * COPYRIGHT (c) 2009. + * On-Line Applications Research Corporation (OAR). + * + * Copyright (c) 2011 Ralf Corsépius, Ulm, Germany. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <limits.h> + +#include <rtems/stringto.h> + +/* + * Instantiate an error checking wrapper for strtol (long) + */ + +rtems_status_code rtems_string_to_long ( + const char *s, + long *n, + char **endptr, + int base +) +{ + long result; + char *end; + + if ( !n ) + return RTEMS_INVALID_ADDRESS; + + errno = 0; + *n = 0; + + result = strtol( s, &end, base ); + + if ( endptr ) + *endptr = end; + + if ( end == s ) + return RTEMS_NOT_DEFINED; + + if ( ( errno == ERANGE ) && + (( result == 0 ) || ( result == LONG_MAX ) || ( result == LONG_MIN ))) + return RTEMS_INVALID_NUMBER; + + *n = result; + + return RTEMS_SUCCESSFUL; +} diff --git a/cpukit/libmisc/stringto/stringtolongdouble.c b/cpukit/libmisc/stringto/stringtolongdouble.c new file mode 100644 index 0000000000..776f5e183b --- /dev/null +++ b/cpukit/libmisc/stringto/stringtolongdouble.c @@ -0,0 +1,58 @@ +/* + * COPYRIGHT (c) 2009. + * On-Line Applications Research Corporation (OAR). + * + * Copyright (c) 2011 Ralf Corsépius, Ulm, Germany. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <math.h> + +#include <rtems/stringto.h> + +/* + * Instantiate an error checking wrapper for strtod (double) + */ + +rtems_status_code rtems_string_to_long_double ( + const char *s, + long double *n, + char **endptr +) +{ + long double result; + char *end; + + if ( !n ) + return RTEMS_INVALID_ADDRESS; + + errno = 0; + *n = 0; + + result = strtold( s, &end ); + + if ( endptr ) + *endptr = end; + + if ( end == s ) + return RTEMS_NOT_DEFINED; + + if ( ( errno == ERANGE ) && + (( result == 0 ) || ( result == HUGE_VALL ) || ( result == -HUGE_VALL ))) + return RTEMS_INVALID_NUMBER; + + *n = result; + + return RTEMS_SUCCESSFUL; +} diff --git a/cpukit/libmisc/stringto/stringtolonglong.c b/cpukit/libmisc/stringto/stringtolonglong.c new file mode 100644 index 0000000000..d291262477 --- /dev/null +++ b/cpukit/libmisc/stringto/stringtolonglong.c @@ -0,0 +1,68 @@ +/* + * COPYRIGHT (c) 2009. + * On-Line Applications Research Corporation (OAR). + * + * Copyright (c) 2011 Ralf Corsépius, Ulm, Germany. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <limits.h> + +#include <rtems/stringto.h> + +/* c99 has LLONG_MAX instead of LONG_LONG_MAX */ +#ifndef LONG_LONG_MAX +#define LONG_LONG_MAX LLONG_MAX +#endif +/* c99 has LLONG_MIN instead of LONG_LONG_MIN */ +#ifndef LONG_LONG_MIN +#define LONG_LONG_MIN LLONG_MIN +#endif + +/* + * Instantiate an error checking wrapper for strtoll (long long) + */ + +rtems_status_code rtems_string_to_long_long ( + const char *s, + long long *n, + char **endptr, + int base +) +{ + long long result; + char *end; + + if ( !n ) + return RTEMS_INVALID_ADDRESS; + + errno = 0; + *n = 0; + + result = strtoll( s, &end, base ); + + if ( endptr ) + *endptr = end; + + if ( end == s ) + return RTEMS_NOT_DEFINED; + + if ( ( errno == ERANGE ) && + (( result == 0 ) || ( result == LONG_LONG_MAX ) || ( result == LONG_LONG_MIN ))) + return RTEMS_INVALID_NUMBER; + + *n = result; + + return RTEMS_SUCCESSFUL; +} diff --git a/cpukit/libmisc/stringto/stringtopointer.c b/cpukit/libmisc/stringto/stringtopointer.c new file mode 100644 index 0000000000..3a99baa3b5 --- /dev/null +++ b/cpukit/libmisc/stringto/stringtopointer.c @@ -0,0 +1,47 @@ +/* + * COPYRIGHT (c) 2009. + * On-Line Applications Research Corporation (OAR). + * + * Copyright (c) 2011 Ralf Corsépius, Ulm, Germany. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <limits.h> +#include <stdint.h> + +#include <rtems/stringto.h> + +/* + * Instantiate an error checking wrapper for strtoul/strtoull (void *) + */ + +#if (UINTPTR_MAX == ULONG_MAX) +#define STRTOFUNC(a,b,c) rtems_string_to_unsigned_long(a, (unsigned long*) b, c, 0) +#elif (UINTPTR_MAX == ULONG_LONG_MAX) +#define STRTOFUNC(a,b,c) rtems_string_to_unsigned_long_long(a, (unsigned long long*) b, c, 0) +#elif (UINTPTR_MAX == UINT_MAX) +#define STRTOFUNC(a,b,c) rtems_string_to_unsigned_int(a, (unsigned int*) b, c, 0) +#else +/* Fallback to unsigned long */ +#define STRTOFUNC(a,b,c) rtems_string_to_unsigned_long(a, (unsigned long*) b, c, 0) +#endif + +rtems_status_code rtems_string_to_pointer ( + const char *s, + void **n, + char **endptr +) +{ + return STRTOFUNC( s, n, endptr ); +} diff --git a/cpukit/libmisc/stringto/stringtounsignedchar.c b/cpukit/libmisc/stringto/stringtounsignedchar.c new file mode 100644 index 0000000000..5223b03fb7 --- /dev/null +++ b/cpukit/libmisc/stringto/stringtounsignedchar.c @@ -0,0 +1,66 @@ +/* + * COPYRIGHT (c) 2009. + * On-Line Applications Research Corporation (OAR). + * + * Copyright (c) 2011 Ralf Corsépius, Ulm, Germany. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <limits.h> + +#include <rtems/stringto.h> + +/* + * Instantiate an error checking wrapper for strtoul (unsigned char) + */ + +rtems_status_code rtems_string_to_unsigned_char ( + const char *s, + unsigned char *n, + char **endptr, + int base +) +{ + unsigned long result; + char *end; + + if ( !n ) + return RTEMS_INVALID_ADDRESS; + + errno = 0; + *n = 0; + + result = strtoul( s, &end, base ); + + if ( endptr ) + *endptr = end; + + if ( end == s ) + return RTEMS_NOT_DEFINED; + + if ( ( errno == ERANGE ) && + (( result == 0 ) || ( result == ULONG_MAX ))) + return RTEMS_INVALID_NUMBER; + +#if (UCHAR_MAX < ULONG_MAX) + if ( result > UCHAR_MAX ) { + errno = ERANGE; + return RTEMS_INVALID_NUMBER; + } +#endif + + *n = result; + + return RTEMS_SUCCESSFUL; +} diff --git a/cpukit/libmisc/stringto/stringtounsignedint.c b/cpukit/libmisc/stringto/stringtounsignedint.c new file mode 100644 index 0000000000..9821beff87 --- /dev/null +++ b/cpukit/libmisc/stringto/stringtounsignedint.c @@ -0,0 +1,66 @@ +/* + * COPYRIGHT (c) 2009. + * On-Line Applications Research Corporation (OAR). + * + * Copyright (c) 2011 Ralf Corsépius, Ulm, Germany. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <limits.h> + +#include <rtems/stringto.h> + +/* + * Instantiate an error checking wrapper for strtoul (unsigned int) + */ + +rtems_status_code rtems_string_to_unsigned_int ( + const char *s, + unsigned int *n, + char **endptr, + int base +) +{ + unsigned long result; + char *end; + + if ( !n ) + return RTEMS_INVALID_ADDRESS; + + errno = 0; + *n = 0; + + result = strtoul( s, &end, base ); + + if ( endptr ) + *endptr = end; + + if ( end == s ) + return RTEMS_NOT_DEFINED; + + if ( ( errno == ERANGE ) && + (( result == 0 ) || ( result == ULONG_MAX ))) + return RTEMS_INVALID_NUMBER; + +#if (UINT_MAX < ULONG_MAX) + if ( result > UINT_MAX ) { + errno = ERANGE; + return RTEMS_INVALID_NUMBER; + } +#endif + + *n = result; + + return RTEMS_SUCCESSFUL; +} diff --git a/cpukit/libmisc/stringto/stringtounsignedlong.c b/cpukit/libmisc/stringto/stringtounsignedlong.c new file mode 100644 index 0000000000..11c374c486 --- /dev/null +++ b/cpukit/libmisc/stringto/stringtounsignedlong.c @@ -0,0 +1,59 @@ +/* + * COPYRIGHT (c) 2009. + * On-Line Applications Research Corporation (OAR). + * + * Copyright (c) 2011 Ralf Corsépius, Ulm, Germany. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <limits.h> + +#include <rtems/stringto.h> + +/* + * Instantiate an error checking wrapper for strtoul (unsigned long) + */ + +rtems_status_code rtems_string_to_unsigned_long ( + const char *s, + unsigned long *n, + char **endptr, + int base +) +{ + unsigned long result; + char *end; + + if ( !n ) + return RTEMS_INVALID_ADDRESS; + + errno = 0; + *n = 0; + + result = strtoul( s, &end, base ); + + if ( endptr ) + *endptr = end; + + if ( end == s ) + return RTEMS_NOT_DEFINED; + + if ( ( errno == ERANGE ) && + (( result == 0 ) || ( result == ULONG_MAX ))) + return RTEMS_INVALID_NUMBER; + + *n = result; + + return RTEMS_SUCCESSFUL; +} diff --git a/cpukit/libmisc/stringto/stringtounsignedlonglong.c b/cpukit/libmisc/stringto/stringtounsignedlonglong.c new file mode 100644 index 0000000000..61c6e2e063 --- /dev/null +++ b/cpukit/libmisc/stringto/stringtounsignedlonglong.c @@ -0,0 +1,64 @@ +/* + * COPYRIGHT (c) 2009. + * On-Line Applications Research Corporation (OAR). + * + * Copyright (c) 2011 Ralf Corsépius, Ulm, Germany. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <limits.h> + +#include <rtems/stringto.h> + +/* c99 has ULLONG_MAX instead of ULONG_LONG_MAX */ +#ifndef ULONG_LONG_MAX +#define ULONG_LONG_MAX ULLONG_MAX +#endif + +/* + * Instantiate an error checking wrapper for strtoull (unsigned long long) + */ + +rtems_status_code rtems_string_to_unsigned_long_long ( + const char *s, + unsigned long long *n, + char **endptr, + int base +) +{ + unsigned long long result; + char *end; + + if ( !n ) + return RTEMS_INVALID_ADDRESS; + + errno = 0; + *n = 0; + + result = strtoull( s, &end, base ); + + if ( endptr ) + *endptr = end; + + if ( end == s ) + return RTEMS_NOT_DEFINED; + + if ( ( errno == ERANGE ) && + (( result == 0 ) || ( result == ULONG_LONG_MAX ))) + return RTEMS_INVALID_NUMBER; + + *n = result; + + return RTEMS_SUCCESSFUL; +} diff --git a/cpukit/libmisc/untar/README b/cpukit/libmisc/untar/README new file mode 100644 index 0000000000..24c9e7fa62 --- /dev/null +++ b/cpukit/libmisc/untar/README @@ -0,0 +1,26 @@ +# +# +# untar information +# +# Author: Jake Janovetz 7.6.1999 +# +# $Id$ +# + +untar.c contains two procedures for extracting files from a UNIX +tar file: + + int Untar_FromMemory(unsigned char *tar_buf, unsigned long size); + int Untar_FromFile(char *tar_name); + +Untar_FromMemory(...) takes its input from a chunk of allocated memory. +This is particularly useful when the tar is stored in Flash memory or +comes from the FTP daemon by way of a hook. + +Untar_FromFile(...) is identical except the source is from an existing +file. The fully qualified filename is passed through char *tar_name. + + + +BUGS: Please email janovetz@uiuc.edu +----- diff --git a/cpukit/libmisc/untar/untar.c b/cpukit/libmisc/untar/untar.c new file mode 100644 index 0000000000..2c5c8e9cf3 --- /dev/null +++ b/cpukit/libmisc/untar/untar.c @@ -0,0 +1,384 @@ +/* FIXME: + * 1. Symbolic links are not created. + * 2. Untar_FromMemory uses FILE *fp. + * 3. How to determine end of archive? + * + * Written by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <rtems/untar.h> +#include <rtems/bspIo.h> + + +/************************************************************************** + * TAR file format: + * + * Offset Length Contents + * 0 100 bytes File name ('\0' terminated, 99 maxmum length) + * 100 8 bytes File mode (in octal ascii) + * 108 8 bytes User ID (in octal ascii) + * 116 8 bytes Group ID (in octal ascii) + * 124 12 bytes File size (s) (in octal ascii) + * 136 12 bytes Modify time (in octal ascii) + * 148 8 bytes Header checksum (in octal ascii) + * 156 1 bytes Link flag + * 157 100 bytes Linkname ('\0' terminated, 99 maxmum length) + * 257 8 bytes Magic PAX ("ustar\0" + 2 bytes padding) + * 257 8 bytes Magic GNU tar ("ustar \0") + * 265 32 bytes User name ('\0' terminated, 31 maxmum length) + * 297 32 bytes Group name ('\0' terminated, 31 maxmum length) + * 329 8 bytes Major device ID (in octal ascii) + * 337 8 bytes Minor device ID (in octal ascii) + * 345 155 bytes Prefix + * 512 (s+p)bytes File contents (s+p) := (((s) + 511) & ~511), + * round up to 512 bytes + * + * Checksum: + * int i, sum; + * char* header = tar_header_pointer; + * sum = 0; + * for(i = 0; i < 512; i++) + * sum += 0xFF & header[i]; + *************************************************************************/ + +#define MAX_NAME_FIELD_SIZE 99 + +#define MIN(a,b) ((a)>(b)?(b):(a)) + + +/************************************************************************** + * This converts octal ASCII number representations into an + * unsigned long. Only support 32-bit numbers for now. + *************************************************************************/ +unsigned long +_rtems_octal2ulong( + const char *octascii, + size_t len +) +{ + size_t i; + unsigned long num; + + num = 0; + for (i=0; i < len; i++) + { + if ((octascii[i] < '0') || (octascii[i] > '9')) + { + continue; + } + num = num * 8 + ((unsigned long)(octascii[i] - '0')); + } + return(num); +} + + +/************************************************************************** + * Function: Untar_FromMemory * + ************************************************************************** + * Description: * + * * + * This is a simple subroutine used to rip links, directories, and * + * files out of a block of memory. * + * * + * * + * Inputs: * + * * + * void * tar_buf - Pointer to TAR buffer. * + * size_t size - Length of TAR buffer. * + * * + * * + * Output: * + * * + * int - UNTAR_SUCCESSFUL (0) on successful completion. * + * UNTAR_INVALID_CHECKSUM for an invalid header checksum. * + * UNTAR_INVALID_HEADER for an invalid header. * + * * + **************************************************************************/ +int +Untar_FromMemory( + void *tar_buf, + size_t size +) +{ + FILE *fp; + const char *tar_ptr = (const char *)tar_buf; + const char *bufr; + size_t n; + char fname[100]; + char linkname[100]; + int sum; + int hdr_chksum; + int retval; + unsigned long ptr; + unsigned long i; + unsigned long nblocks; + unsigned long file_size; + unsigned char linkflag; + + + ptr = 0; + while (1) + { + if (ptr + 512 > size) + { + retval = UNTAR_SUCCESSFUL; + break; + } + + /* Read the header */ + bufr = &tar_ptr[ptr]; + ptr += 512; + if (strncmp(&bufr[257], "ustar", 5)) + { + retval = UNTAR_SUCCESSFUL; + break; + } + + strncpy(fname, bufr, MAX_NAME_FIELD_SIZE); + fname[MAX_NAME_FIELD_SIZE] = '\0'; + + linkflag = bufr[156]; + file_size = _rtems_octal2ulong(&bufr[124], 12); + + /****************************************************************** + * Compute the TAR checksum and check with the value in + * the archive. The checksum is computed over the entire + * header, but the checksum field is substituted with blanks. + ******************************************************************/ + hdr_chksum = _rtems_octal2ulong(&bufr[148], 8); + sum = _rtems_tar_header_checksum(bufr); + + if (sum != hdr_chksum) + { + retval = UNTAR_INVALID_CHECKSUM; + break; + } + + + /****************************************************************** + * We've decoded the header, now figure out what it contains and + * do something with it. + *****************************************************************/ + if (linkflag == SYMTYPE) + { + strncpy(linkname, &bufr[157], MAX_NAME_FIELD_SIZE); + linkname[MAX_NAME_FIELD_SIZE] = '\0'; + symlink(linkname, fname); + } + else if (linkflag == REGTYPE) + { + nblocks = (((file_size) + 511) & ~511) / 512; + if ((fp = fopen(fname, "w")) == NULL) + { + printk("Untar: failed to create file %s\n", fname); + ptr += 512 * nblocks; + } + else + { + unsigned long sizeToGo = file_size; + size_t len; + + /*************************************************************** + * Read out the data. There are nblocks of data where nblocks + * is the file_size rounded to the nearest 512-byte boundary. + **************************************************************/ + for (i=0; i<nblocks; i++) + { + len = ((sizeToGo < 512L)?(sizeToGo):(512L)); + n = fwrite(&tar_ptr[ptr], 1, len, fp); + if (n != len) + { + printk("untar: Error during write\n"); + break; + } + ptr += 512; + sizeToGo -= n; + } + fclose(fp); + } + } + else if (linkflag == DIRTYPE) + { + mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO); + } + } + + return(retval); +} + + +/************************************************************************** + * Function: Untar_FromFile * + ************************************************************************** + * Description: * + * * + * This is a simple subroutine used to rip links, directories, and * + * files out of a TAR file. * + * * + * * + * Inputs: * + * * + * const char *tar_name - TAR filename. * + * * + * * + * Output: * + * * + * int - UNTAR_SUCCESSFUL (0) on successful completion. * + * UNTAR_INVALID_CHECKSUM for an invalid header checksum. * + * UNTAR_INVALID_HEADER for an invalid header. * + * * + ************************************************************************** + * Change History: * + * 12/30/1998 - Creation (JWJ) * + *************************************************************************/ +int +Untar_FromFile( + const char *tar_name +) +{ + int fd; + char *bufr; + ssize_t n; + char fname[100]; + char linkname[100]; + int sum; + int hdr_chksum; + int retval; + unsigned long i; + unsigned long nblocks; + unsigned long size; + unsigned char linkflag; + + retval = UNTAR_SUCCESSFUL; + + if ((fd = open(tar_name, O_RDONLY)) < 0) { + return UNTAR_FAIL; + } + + bufr = (char *)malloc(512); + if (bufr == NULL) { + return(UNTAR_FAIL); + } + + while (1) + { + /* Read the header */ + /* If the header read fails, we just consider it the end + of the tarfile. */ + if ((n = read(fd, bufr, 512)) != 512) + { + break; + } + + if (strncmp(&bufr[257], "ustar", 5)) + { + break; + } + + strncpy(fname, bufr, MAX_NAME_FIELD_SIZE); + fname[MAX_NAME_FIELD_SIZE] = '\0'; + + linkflag = bufr[156]; + size = _rtems_octal2ulong(&bufr[124], 12); + + /****************************************************************** + * Compute the TAR checksum and check with the value in + * the archive. The checksum is computed over the entire + * header, but the checksum field is substituted with blanks. + ******************************************************************/ + hdr_chksum = _rtems_octal2ulong(&bufr[148], 8); + sum = _rtems_tar_header_checksum(bufr); + + if (sum != hdr_chksum) + { + retval = UNTAR_INVALID_CHECKSUM; + break; + } + + /****************************************************************** + * We've decoded the header, now figure out what it contains and + * do something with it. + *****************************************************************/ + if (linkflag == SYMTYPE) + { + strncpy(linkname, &bufr[157], MAX_NAME_FIELD_SIZE); + linkname[MAX_NAME_FIELD_SIZE] = '\0'; + symlink(linkname,fname); + } + else if (linkflag == REGTYPE) + { + int out_fd; + + /****************************************************************** + * Read out the data. There are nblocks of data where nblocks + * is the size rounded to the nearest 512-byte boundary. + *****************************************************************/ + nblocks = (((size) + 511) & ~511) / 512; + + if ((out_fd = creat(fname, 0644)) == -1) + { + for (i=0; i<nblocks; i++) + { + n = read(fd, bufr, 512); + } + } + else + { + for (i=0; i<nblocks; i++) + { + n = read(fd, bufr, 512); + n = MIN(n, size - i*512); + write(out_fd, bufr, n); + } + close(out_fd); + } + } + else if (linkflag == DIRTYPE) + { + mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO); + } + } + free(bufr); + close(fd); + + return(retval); +} + +/************************************************************************ + * Compute the TAR checksum and check with the value in + * the archive. The checksum is computed over the entire + * header, but the checksum field is substituted with blanks. + ************************************************************************/ +int +_rtems_tar_header_checksum( + const char *bufr +) +{ + int i, sum; + + sum = 0; + for (i=0; i<512; i++) + { + if ((i >= 148) && (i < 156)) + sum += 0xff & ' '; + else + sum += 0xff & bufr[i]; + } + return(sum); +} diff --git a/cpukit/libmisc/untar/untar.h b/cpukit/libmisc/untar/untar.h new file mode 100644 index 0000000000..264753f2f7 --- /dev/null +++ b/cpukit/libmisc/untar/untar.h @@ -0,0 +1,49 @@ +/* + * Written by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef _RTEMS_UNTAR_H +#define _RTEMS_UNTAR_H + +#include <stddef.h> +#include <tar.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define UNTAR_SUCCESSFUL 0 +#define UNTAR_FAIL 1 +#define UNTAR_INVALID_CHECKSUM 2 +#define UNTAR_INVALID_HEADER 3 + + +int Untar_FromMemory(void *tar_buf, size_t size); +int Untar_FromFile(const char *tar_name); + +/************************************************************************** + * This converts octal ASCII number representations into an + * unsigned long. Only support 32-bit numbers for now. + *************************************************************************/ +extern unsigned long +_rtems_octal2ulong(const char *octascii, size_t len); + +/************************************************************************ + * Compute the TAR checksum and check with the value in + * the archive. The checksum is computed over the entire + * header, but the checksum field is substituted with blanks. + ************************************************************************/ +extern int +_rtems_tar_header_checksum(const char *bufr); + +#ifdef __cplusplus +} +#endif + +#endif /* _RTEMS_UNTAR_H */ diff --git a/cpukit/libmisc/uuid/COPYING b/cpukit/libmisc/uuid/COPYING new file mode 100644 index 0000000000..2f17068367 --- /dev/null +++ b/cpukit/libmisc/uuid/COPYING @@ -0,0 +1,25 @@ +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, and the entire permission notice in its entirety, + including the disclaimer of warranties. +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. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF +WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/cpukit/libmisc/uuid/README b/cpukit/libmisc/uuid/README new file mode 100644 index 0000000000..6b5c9eee97 --- /dev/null +++ b/cpukit/libmisc/uuid/README @@ -0,0 +1,14 @@ +uuid - DCE compatible Universally Unique Identifier library + +The UUID library is used to generate unique identifiers for objects that may be +accessible beyond the local system. This library generates UUIDs compatible +with those created by the Open Software Foundation (OSF) Distributed Computing +Environment (DCE) utility uuidgen. + +The UUIDs generated by this library can be reasonably expected to be unique +within a system, and unique across all systems. They could be used, for +instance, to generate unique HTTP cookies across multiple web servers without +communication between the servers, and without fear of a name clash. + +This code is from the E2fsprogs project: http://e2fsprogs.sourceforge.net. +The package was e2fsprogs-libs-1.41.5.tar.gz. diff --git a/cpukit/libmisc/uuid/clear.c b/cpukit/libmisc/uuid/clear.c new file mode 100644 index 0000000000..5a1d188f6e --- /dev/null +++ b/cpukit/libmisc/uuid/clear.c @@ -0,0 +1,47 @@ +/* + * clear.c -- Clear a UUID + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include "uuidP.h" + +void uuid_clear(uuid_t uu) +{ + memset(uu, 0, 16); +} + diff --git a/cpukit/libmisc/uuid/compare.c b/cpukit/libmisc/uuid/compare.c new file mode 100644 index 0000000000..888121f0ff --- /dev/null +++ b/cpukit/libmisc/uuid/compare.c @@ -0,0 +1,59 @@ +/* + * compare.c --- compare whether or not two UUID's are the same + * + * Returns 0 if the two UUID's are different, and 1 if they are the same. + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include "uuidP.h" + +#define UUCMP(u1,u2) if (u1 != u2) return((u1 < u2) ? -1 : 1); + +int uuid_compare(const uuid_t uu1, const uuid_t uu2) +{ + struct uuid uuid1, uuid2; + + uuid_unpack(uu1, &uuid1); + uuid_unpack(uu2, &uuid2); + + UUCMP(uuid1.time_low, uuid2.time_low); + UUCMP(uuid1.time_mid, uuid2.time_mid); + UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version); + UUCMP(uuid1.clock_seq, uuid2.clock_seq); + return memcmp(uuid1.node, uuid2.node, 6); +} + diff --git a/cpukit/libmisc/uuid/copy.c b/cpukit/libmisc/uuid/copy.c new file mode 100644 index 0000000000..abf09d6fb3 --- /dev/null +++ b/cpukit/libmisc/uuid/copy.c @@ -0,0 +1,49 @@ +/* + * copy.c --- copy UUIDs + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "uuidP.h" + +void uuid_copy(uuid_t dst, const uuid_t src) +{ + unsigned char *cp1; + const unsigned char *cp2; + int i; + + for (i=0, cp1 = dst, cp2 = src; i < 16; i++) + *cp1++ = *cp2++; +} diff --git a/cpukit/libmisc/uuid/gen_uuid.c b/cpukit/libmisc/uuid/gen_uuid.c new file mode 100644 index 0000000000..7993ee5332 --- /dev/null +++ b/cpukit/libmisc/uuid/gen_uuid.c @@ -0,0 +1,670 @@ +/* + * gen_uuid.c --- generate a DCE-compatible uuid + * + * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +/* + * Force inclusion of SVID stuff since we need it if we're compiling in + * gcc-wall wall mode + */ +#define _SVID_SOURCE + +#ifdef _WIN32 +#define _WIN32_WINNT 0x0500 +#include <windows.h> +#define UUID MYUUID +#endif +#include <stdio.h> +#include <limits.h> /* for CHAR_BIT */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/wait.h> +#include <sys/stat.h> +#ifdef HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_NET_IF_DL_H +#include <net/if_dl.h> +#endif +#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H) +#include <sys/syscall.h> +#endif +#ifdef HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif + +#include "uuidP.h" +#include "uuidd.h" + +#ifdef HAVE_SRANDOM +#define srand(x) srandom(x) +#define rand() random() +#endif + +#ifdef TLS +#define THREAD_LOCAL static TLS +#else +#define THREAD_LOCAL static +#endif + +#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48) +#define DO_JRAND_MIX +THREAD_LOCAL unsigned short jrand_seed[3]; +#endif + +#ifdef _WIN32 +static void gettimeofday (struct timeval *tv, void *dummy) +{ + FILETIME ftime; + uint64_t n; + + GetSystemTimeAsFileTime (&ftime); + n = (((uint64_t) ftime.dwHighDateTime << 32) + + (uint64_t) ftime.dwLowDateTime); + if (n) { + n /= 10; + n -= ((369 * 365 + 89) * (uint64_t) 86400) * 1000000; + } + + tv->tv_sec = n / 1000000; + tv->tv_usec = n % 1000000; +} + +static int getuid (void) +{ + return 1; +} +#endif + +static int get_random_fd(void) +{ + struct timeval tv; + static int fd = -2; + int i; + + if (fd == -2) { + gettimeofday(&tv, 0); +#ifndef _WIN32 + fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (fd >= 0) { + i = fcntl(fd, F_GETFD); + if (i >= 0) + fcntl(fd, F_SETFD, i | FD_CLOEXEC); + } +#endif + srand((getpid() << ((sizeof(pid_t)*CHAR_BIT)>>1)) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec); +#ifdef DO_JRAND_MIX + jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF); + jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF); + jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16; +#endif + } + /* Crank the random number generator a few times */ + gettimeofday(&tv, 0); + for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--) + rand(); + return fd; +} + + +/* + * Generate a series of random bytes. Use /dev/urandom if possible, + * and if not, use srandom/random. + */ +static void get_random_bytes(void *buf, int nbytes) +{ + int i, n = nbytes, fd = get_random_fd(); + int lose_counter = 0; + unsigned char *cp = (unsigned char *) buf; +#ifdef DO_JRAND_MIX + unsigned short tmp_seed[3]; +#endif + + if (fd >= 0) { + while (n > 0) { + i = read(fd, cp, n); + if (i <= 0) { + if (lose_counter++ > 16) + break; + continue; + } + n -= i; + cp += i; + lose_counter = 0; + } + } + + /* + * We do this all the time, but this is the only source of + * randomness if /dev/random/urandom is out to lunch. + */ + for (cp = buf, i = 0; i < nbytes; i++) + *cp++ ^= (rand() >> 7) & 0xFF; +#ifdef DO_JRAND_MIX + memcpy(tmp_seed, jrand_seed, sizeof(tmp_seed)); + jrand_seed[2] = jrand_seed[2] ^ syscall(__NR_gettid); + for (cp = buf, i = 0; i < nbytes; i++) + *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF; + memcpy(jrand_seed, tmp_seed, + sizeof(jrand_seed)-sizeof(unsigned short)); +#endif + + return; +} + +/* + * Get the ethernet hardware address, if we can find it... + * + * XXX for a windows version, probably should use GetAdaptersInfo: + * http://www.codeguru.com/cpp/i-n/network/networkinformation/article.php/c5451 + * commenting out get_node_id just to get gen_uuid to compile under windows + * is not the right way to go! + */ +static int get_node_id(unsigned char *node_id) +{ +#ifdef HAVE_NET_IF_H + int sd; + struct ifreq ifr, *ifrp; + struct ifconf ifc; + char buf[1024]; + int n, i; + unsigned char *a; +#ifdef HAVE_NET_IF_DL_H + struct sockaddr_dl *sdlp; +#endif + +/* + * BSD 4.4 defines the size of an ifreq to be + * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len + * However, under earlier systems, sa_len isn't present, so the size is + * just sizeof(struct ifreq) + */ +#ifdef HAVE_SA_LEN +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif +#define ifreq_size(i) max(sizeof(struct ifreq),\ + sizeof((i).ifr_name)+(i).ifr_addr.sa_len) +#else +#define ifreq_size(i) sizeof(struct ifreq) +#endif /* HAVE_SA_LEN*/ + + sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sd < 0) { + return -1; + } + memset(buf, 0, sizeof(buf)); + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) { + close(sd); + return -1; + } + n = ifc.ifc_len; + for (i = 0; i < n; i+= ifreq_size(*ifrp) ) { + ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i); + strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ); +#ifdef SIOCGIFHWADDR + if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) + continue; + a = (unsigned char *) &ifr.ifr_hwaddr.sa_data; +#else +#ifdef SIOCGENADDR + if (ioctl(sd, SIOCGENADDR, &ifr) < 0) + continue; + a = (unsigned char *) ifr.ifr_enaddr; +#else +#ifdef HAVE_NET_IF_DL_H + sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr; + if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6)) + continue; + a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen]; +#else + /* + * XXX we don't have a way of getting the hardware + * address + */ + close(sd); + return 0; +#endif /* HAVE_NET_IF_DL_H */ +#endif /* SIOCGENADDR */ +#endif /* SIOCGIFHWADDR */ + if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) + continue; + if (node_id) { + memcpy(node_id, a, 6); + close(sd); + return 1; + } + } + close(sd); +#endif + return 0; +} + +/* Assume that the gettimeofday() has microsecond granularity */ +#define MAX_ADJUSTMENT 10 + +static int get_clock(uint32_t *clock_high, uint32_t *clock_low, + uint16_t *ret_clock_seq, int *num) +{ + THREAD_LOCAL int adjustment = 0; + THREAD_LOCAL struct timeval last = {0, 0}; + THREAD_LOCAL int state_fd = -2; + THREAD_LOCAL FILE *state_f; + THREAD_LOCAL uint16_t clock_seq; + struct timeval tv; + struct flock fl; + uint64_t clock_reg; + mode_t save_umask; + int len; + + if (state_fd == -2) { + save_umask = umask(0); + state_fd = open("/var/lib/libuuid/clock.txt", + O_RDWR|O_CREAT, 0660); + (void) umask(save_umask); + state_f = fdopen(state_fd, "r+"); + if (!state_f) { + close(state_fd); + state_fd = -1; + } + } + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fl.l_pid = 0; + if (state_fd >= 0) { + rewind(state_f); + while (fcntl(state_fd, F_SETLKW, &fl) < 0) { + if ((errno == EAGAIN) || (errno == EINTR)) + continue; + fclose(state_f); + close(state_fd); + state_fd = -1; + break; + } + } + if (state_fd >= 0) { + unsigned int cl; + unsigned long tv1, tv2; + int a; + + if (fscanf(state_f, "clock: %04x tv: %lu %lu adj: %d\n", + &cl, &tv1, &tv2, &a) == 4) { + clock_seq = cl & 0x3FFF; + last.tv_sec = tv1; + last.tv_usec = tv2; + adjustment = a; + } + } + + if ((last.tv_sec == 0) && (last.tv_usec == 0)) { + get_random_bytes(&clock_seq, sizeof(clock_seq)); + clock_seq &= 0x3FFF; + gettimeofday(&last, 0); + last.tv_sec--; + } + +try_again: + gettimeofday(&tv, 0); + if ((tv.tv_sec < last.tv_sec) || + ((tv.tv_sec == last.tv_sec) && + (tv.tv_usec < last.tv_usec))) { + clock_seq = (clock_seq+1) & 0x3FFF; + adjustment = 0; + last = tv; + } else if ((tv.tv_sec == last.tv_sec) && + (tv.tv_usec == last.tv_usec)) { + if (adjustment >= MAX_ADJUSTMENT) + goto try_again; + adjustment++; + } else { + adjustment = 0; + last = tv; + } + + clock_reg = tv.tv_usec*10 + adjustment; + clock_reg += ((uint64_t) tv.tv_sec)*10000000; + clock_reg += (((uint64_t) 0x01B21DD2) << 32) + 0x13814000; + + if (num && (*num > 1)) { + adjustment += *num - 1; + last.tv_usec += adjustment / 10; + adjustment = adjustment % 10; + last.tv_sec += last.tv_usec / 1000000; + last.tv_usec = last.tv_usec % 1000000; + } + + if (state_fd > 0) { + rewind(state_f); + len = fprintf(state_f, + "clock: %04x tv: %016lu %08lu adj: %08d\n", + clock_seq, last.tv_sec, last.tv_usec, adjustment); + fflush(state_f); + if (ftruncate(state_fd, len) < 0) { + fprintf(state_f, " \n"); + fflush(state_f); + } + rewind(state_f); + fl.l_type = F_UNLCK; + fcntl(state_fd, F_SETLK, &fl); + } + + *clock_high = clock_reg >> 32; + *clock_low = clock_reg; + *ret_clock_seq = clock_seq; + return 0; +} + +/* unused */ +#if defined(USE_UUIDD) && defined(HAVE_SYS_UN_H) +static ssize_t read_all(int fd, char *buf, size_t count) +{ + ssize_t ret; + ssize_t c = 0; + + memset(buf, 0, count); + while (count > 0) { + ret = read(fd, buf, count); + if (ret < 0) { + if ((errno == EAGAIN) || (errno == EINTR)) + continue; + return -1; + } + count -= ret; + buf += ret; + c += ret; + } + return c; +} +#endif + +#if defined(USE_UUIDD) && defined(HAVE_SYS_UN_H) + +/* + * Close all file descriptors + */ +static void close_all_fds(void) +{ + int i, max; + +#if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX) + max = sysconf(_SC_OPEN_MAX); +#elif defined(HAVE_GETDTABLESIZE) + max = getdtablesize(); +#elif defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) + struct rlimit rl; + + getrlimit(RLIMIT_NOFILE, &rl); + max = rl.rlim_cur; +#else + max = OPEN_MAX; +#endif + + for (i=0; i < max; i++) + close(i); +} + +#endif + + +/* + * Try using the uuidd daemon to generate the UUID + * + * Returns 0 on success, non-zero on failure. + */ +static int get_uuid_via_daemon(int op, uuid_t out, int *num) +{ +#if defined(USE_UUIDD) && defined(HAVE_SYS_UN_H) + char op_buf[64]; + int op_len; + int s; + ssize_t ret; + int32_t reply_len = 0, expected = 16; + struct sockaddr_un srv_addr; + pid_t pid; + static const char *uuidd_path = UUIDD_PATH; + static int access_ret = -2; + static int start_attempts = 0; + + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + return -1; + + srv_addr.sun_family = AF_UNIX; + strcpy(srv_addr.sun_path, UUIDD_SOCKET_PATH); + + if (connect(s, (const struct sockaddr *) &srv_addr, + sizeof(struct sockaddr_un)) < 0) { + if (access_ret == -2) + access_ret = access(uuidd_path, X_OK); + if (access_ret == 0 && start_attempts++ < 5) { + if ((pid = fork()) == 0) { + close_all_fds(); + execl(uuidd_path, "uuidd", "-qT", "300", + (char *) NULL); + exit(1); + } + (void) waitpid(pid, 0, 0); + if (connect(s, (const struct sockaddr *) &srv_addr, + sizeof(struct sockaddr_un)) < 0) + goto fail; + } else + goto fail; + } + op_buf[0] = op; + op_len = 1; + if (op == UUIDD_OP_BULK_TIME_UUID) { + memcpy(op_buf+1, num, sizeof(*num)); + op_len += sizeof(*num); + expected += sizeof(*num); + } + + ret = write(s, op_buf, op_len); + if (ret < 1) + goto fail; + + ret = read_all(s, (char *) &reply_len, sizeof(reply_len)); + if (ret < 0) + goto fail; + + if (reply_len != expected) + goto fail; + + ret = read_all(s, op_buf, reply_len); + + if (op == UUIDD_OP_BULK_TIME_UUID) + memcpy(op_buf+16, num, sizeof(int)); + + memcpy(out, op_buf, 16); + + close(s); + return ((ret == expected) ? 0 : -1); + +fail: + close(s); +#endif + return -1; +} + +void uuid__generate_time(uuid_t out, int *num) +{ + static unsigned char node_id[6]; + static int has_init = 0; + struct uuid uu; + uint32_t clock_mid; + + if (!has_init) { + if (get_node_id(node_id) <= 0) { + get_random_bytes(node_id, 6); + /* + * Set multicast bit, to prevent conflicts + * with IEEE 802 addresses obtained from + * network cards + */ + node_id[0] |= 0x01; + } + has_init = 1; + } + get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num); + uu.clock_seq |= 0x8000; + uu.time_mid = (uint16_t) clock_mid; + uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000; + memcpy(uu.node, node_id, 6); + uuid_pack(&uu, out); +} + +void uuid_generate_time(uuid_t out) +{ +#ifdef TLS + THREAD_LOCAL int num = 0; + THREAD_LOCAL struct uuid uu; + THREAD_LOCAL time_t last_time = 0; + time_t now; + + if (num > 0) { + now = time(0); + if (now > last_time+1) + num = 0; + } + if (num <= 0) { + num = 1000; + if (get_uuid_via_daemon(UUIDD_OP_BULK_TIME_UUID, + out, &num) == 0) { + last_time = time(0); + uuid_unpack(out, &uu); + num--; + return; + } + num = 0; + } + if (num > 0) { + uu.time_low++; + if (uu.time_low == 0) { + uu.time_mid++; + if (uu.time_mid == 0) + uu.time_hi_and_version++; + } + num--; + uuid_pack(&uu, out); + return; + } +#else + if (get_uuid_via_daemon(UUIDD_OP_TIME_UUID, out, 0) == 0) + return; +#endif + + uuid__generate_time(out, 0); +} + + +void uuid__generate_random(uuid_t out, int *num) +{ + uuid_t buf; + struct uuid uu; + int i, n; + + if (!num || !*num) + n = 1; + else + n = *num; + + for (i = 0; i < n; i++) { + get_random_bytes(buf, sizeof(buf)); + uuid_unpack(buf, &uu); + + uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000; + uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) + | 0x4000; + uuid_pack(&uu, out); + out += sizeof(uuid_t); + } +} + +void uuid_generate_random(uuid_t out) +{ + int num = 1; + /* No real reason to use the daemon for random uuid's -- yet */ + + uuid__generate_random(out, &num); +} + + +/* + * This is the generic front-end to uuid_generate_random and + * uuid_generate_time. It uses uuid_generate_random only if + * /dev/urandom is available, since otherwise we won't have + * high-quality randomness. + */ +void uuid_generate(uuid_t out) +{ + if (get_random_fd() >= 0) + uuid_generate_random(out); + else + uuid_generate_time(out); +} diff --git a/cpukit/libmisc/uuid/isnull.c b/cpukit/libmisc/uuid/isnull.c new file mode 100644 index 0000000000..11bd765d98 --- /dev/null +++ b/cpukit/libmisc/uuid/isnull.c @@ -0,0 +1,52 @@ +/* + * isnull.c --- Check whether or not the UUID is null + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "uuidP.h" + +/* Returns 1 if the uuid is the NULL uuid */ +int uuid_is_null(const uuid_t uu) +{ + const unsigned char *cp; + int i; + + for (i=0, cp = uu; i < 16; i++) + if (*cp++) + return 0; + return 1; +} + diff --git a/cpukit/libmisc/uuid/pack.c b/cpukit/libmisc/uuid/pack.c new file mode 100644 index 0000000000..d4c9955374 --- /dev/null +++ b/cpukit/libmisc/uuid/pack.c @@ -0,0 +1,73 @@ +/* + * Internal routine for packing UUID's + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include "uuidP.h" + +void uuid_pack(const struct uuid *uu, uuid_t ptr) +{ + uint32_t tmp; + unsigned char *out = ptr; + + tmp = uu->time_low; + out[3] = (unsigned char) tmp; + tmp >>= 8; + out[2] = (unsigned char) tmp; + tmp >>= 8; + out[1] = (unsigned char) tmp; + tmp >>= 8; + out[0] = (unsigned char) tmp; + + tmp = uu->time_mid; + out[5] = (unsigned char) tmp; + tmp >>= 8; + out[4] = (unsigned char) tmp; + + tmp = uu->time_hi_and_version; + out[7] = (unsigned char) tmp; + tmp >>= 8; + out[6] = (unsigned char) tmp; + + tmp = uu->clock_seq; + out[9] = (unsigned char) tmp; + tmp >>= 8; + out[8] = (unsigned char) tmp; + + memcpy(out+10, uu->node, 6); +} + diff --git a/cpukit/libmisc/uuid/parse.c b/cpukit/libmisc/uuid/parse.c new file mode 100644 index 0000000000..d995df1db0 --- /dev/null +++ b/cpukit/libmisc/uuid/parse.c @@ -0,0 +1,83 @@ +/* + * parse.c --- UUID parsing + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> + +#include "uuidP.h" + +int uuid_parse(const char *in, uuid_t uu) +{ + struct uuid uuid; + int i; + const char *cp; + char buf[3]; + + if (strlen(in) != 36) + return -1; + for (i=0, cp = in; i <= 36; i++,cp++) { + if ((i == 8) || (i == 13) || (i == 18) || + (i == 23)) { + if (*cp == '-') + continue; + else + return -1; + } + if (i== 36) + if (*cp == 0) + continue; + if (!isxdigit((unsigned char)*cp)) + return -1; + } + uuid.time_low = strtoul(in, NULL, 16); + uuid.time_mid = strtoul(in+9, NULL, 16); + uuid.time_hi_and_version = strtoul(in+14, NULL, 16); + uuid.clock_seq = strtoul(in+19, NULL, 16); + cp = in+24; + buf[2] = 0; + for (i=0; i < 6; i++) { + buf[0] = *cp++; + buf[1] = *cp++; + uuid.node[i] = strtoul(buf, NULL, 16); + } + + uuid_pack(&uuid, uu); + return 0; +} diff --git a/cpukit/libmisc/uuid/unpack.c b/cpukit/libmisc/uuid/unpack.c new file mode 100644 index 0000000000..4520633b7e --- /dev/null +++ b/cpukit/libmisc/uuid/unpack.c @@ -0,0 +1,67 @@ +/* + * Internal routine for unpacking UUID + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include "uuidP.h" + +void uuid_unpack(const uuid_t in, struct uuid *uu) +{ + const uint8_t *ptr = in; + uint32_t tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_low = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_mid = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_hi_and_version = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->clock_seq = tmp; + + memcpy(uu->node, ptr, 6); +} + diff --git a/cpukit/libmisc/uuid/unparse.c b/cpukit/libmisc/uuid/unparse.c new file mode 100644 index 0000000000..25cf07dc78 --- /dev/null +++ b/cpukit/libmisc/uuid/unparse.c @@ -0,0 +1,80 @@ +/* + * unparse.c -- convert a UUID to string + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> + +#include "uuidP.h" + +static const char *fmt_lower = + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"; + +static const char *fmt_upper = + "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"; + +#ifdef UUID_UNPARSE_DEFAULT_UPPER +#define FMT_DEFAULT fmt_upper +#else +#define FMT_DEFAULT fmt_lower +#endif + +static void uuid_unparse_x(const uuid_t uu, char *out, const char *fmt) +{ + struct uuid uuid; + + uuid_unpack(uu, &uuid); + sprintf(out, fmt, + uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, + uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, + uuid.node[0], uuid.node[1], uuid.node[2], + uuid.node[3], uuid.node[4], uuid.node[5]); +} + +void uuid_unparse_lower(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, fmt_lower); +} + +void uuid_unparse_upper(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, fmt_upper); +} + +void uuid_unparse(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, FMT_DEFAULT); +} diff --git a/cpukit/libmisc/uuid/uuid.h b/cpukit/libmisc/uuid/uuid.h new file mode 100644 index 0000000000..ca846da0f0 --- /dev/null +++ b/cpukit/libmisc/uuid/uuid.h @@ -0,0 +1,103 @@ +/* + * Public include file for the UUID library + * + * Copyright (C) 1996, 1997, 1998 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifndef _UUID_UUID_H +#define _UUID_UUID_H + +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/time.h> +#endif +#include <time.h> + +typedef unsigned char uuid_t[16]; + +/* UUID Variant definitions */ +#define UUID_VARIANT_NCS 0 +#define UUID_VARIANT_DCE 1 +#define UUID_VARIANT_MICROSOFT 2 +#define UUID_VARIANT_OTHER 3 + +/* UUID Type definitions */ +#define UUID_TYPE_DCE_TIME 1 +#define UUID_TYPE_DCE_RANDOM 4 + +/* Allow UUID constants to be defined */ +#ifdef __GNUC__ +#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ + static const uuid_t name __attribute__ ((unused)) = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} +#else +#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ + static const uuid_t name = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* clear.c */ +void uuid_clear(uuid_t uu); + +/* compare.c */ +int uuid_compare(const uuid_t uu1, const uuid_t uu2); + +/* copy.c */ +void uuid_copy(uuid_t dst, const uuid_t src); + +/* gen_uuid.c */ +void uuid_generate(uuid_t out); +void uuid_generate_random(uuid_t out); +void uuid_generate_time(uuid_t out); + +/* isnull.c */ +int uuid_is_null(const uuid_t uu); + +/* parse.c */ +int uuid_parse(const char *in, uuid_t uu); + +/* unparse.c */ +void uuid_unparse(const uuid_t uu, char *out); +void uuid_unparse_lower(const uuid_t uu, char *out); +void uuid_unparse_upper(const uuid_t uu, char *out); + +/* uuid_time.c */ +time_t uuid_time(const uuid_t uu, struct timeval *ret_tv); +int uuid_type(const uuid_t uu); +int uuid_variant(const uuid_t uu); + +#ifdef __cplusplus +} +#endif + +#endif /* _UUID_UUID_H */ diff --git a/cpukit/libmisc/uuid/uuidP.h b/cpukit/libmisc/uuid/uuidP.h new file mode 100644 index 0000000000..d0e24c6942 --- /dev/null +++ b/cpukit/libmisc/uuid/uuidP.h @@ -0,0 +1,63 @@ +/* + * uuid.h -- private header file for uuids + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#else +#include <uuid/uuid_types.h> +#endif +#include <sys/types.h> + +#include "uuid.h" + +/* + * Offset between 15-Oct-1582 and 1-Jan-70 + */ +#define TIME_OFFSET_HIGH 0x01B21DD2 +#define TIME_OFFSET_LOW 0x13814000 + +struct uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint16_t clock_seq; + uint8_t node[6]; +}; + + +/* + * prototypes + */ +void uuid_pack(const struct uuid *uu, uuid_t ptr); +void uuid_unpack(const uuid_t in, struct uuid *uu); diff --git a/cpukit/libmisc/uuid/uuid_time.c b/cpukit/libmisc/uuid/uuid_time.c new file mode 100644 index 0000000000..f9325968df --- /dev/null +++ b/cpukit/libmisc/uuid/uuid_time.c @@ -0,0 +1,175 @@ +/* + * uuid_time.c --- Interpret the time field from a uuid. This program + * violates the UUID abstraction barrier by reaching into the guts + * of a UUID and interpreting it. + * + * Copyright (C) 1998, 1999 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _WIN32 +#define _WIN32_WINNT 0x0500 +#include <windows.h> +#define UUID MYUUID +#endif + +#include <stdio.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <stdlib.h> +#include <sys/types.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <time.h> + +#include "uuidP.h" + +time_t uuid_time(const uuid_t uu, struct timeval *ret_tv) +{ + struct timeval tv; + struct uuid uuid; + uint32_t high; + uint64_t clock_reg; + + uuid_unpack(uu, &uuid); + + high = uuid.time_mid | ((uint32_t)(uuid.time_hi_and_version & 0xFFF) << 16); + clock_reg = uuid.time_low | ((uint64_t) high << 32); + + clock_reg -= (((uint64_t) 0x01B21DD2) << 32) + 0x13814000; + tv.tv_sec = clock_reg / 10000000; + tv.tv_usec = (clock_reg % 10000000) / 10; + + if (ret_tv) + *ret_tv = tv; + + return tv.tv_sec; +} + +int uuid_type(const uuid_t uu) +{ + struct uuid uuid; + + uuid_unpack(uu, &uuid); + return ((uuid.time_hi_and_version >> 12) & 0xF); +} + +int uuid_variant(const uuid_t uu) +{ + struct uuid uuid; + int var; + + uuid_unpack(uu, &uuid); + var = uuid.clock_seq; + + if ((var & 0x8000) == 0) + return UUID_VARIANT_NCS; + if ((var & 0x4000) == 0) + return UUID_VARIANT_DCE; + if ((var & 0x2000) == 0) + return UUID_VARIANT_MICROSOFT; + return UUID_VARIANT_OTHER; +} + +#ifdef DEBUG +static const char *variant_string(int variant) +{ + switch (variant) { + case UUID_VARIANT_NCS: + return "NCS"; + case UUID_VARIANT_DCE: + return "DCE"; + case UUID_VARIANT_MICROSOFT: + return "Microsoft"; + default: + return "Other"; + } +} + + +int +main(int argc, char **argv) +{ + uuid_t buf; + time_t time_reg; + struct timeval tv; + int type, variant; + + if (argc != 2) { + fprintf(stderr, "Usage: %s uuid\n", argv[0]); + exit(1); + } + if (uuid_parse(argv[1], buf)) { + fprintf(stderr, "Invalid UUID: %s\n", argv[1]); + exit(1); + } + variant = uuid_variant(buf); + type = uuid_type(buf); + time_reg = uuid_time(buf, &tv); + + printf("UUID variant is %d (%s)\n", variant, variant_string(variant)); + if (variant != UUID_VARIANT_DCE) { + printf("Warning: This program only knows how to interpret " + "DCE UUIDs.\n\tThe rest of the output is likely " + "to be incorrect!!\n"); + } + printf("UUID type is %d", type); + switch (type) { + case 1: + printf(" (time based)\n"); + break; + case 2: + printf(" (DCE)\n"); + break; + case 3: + printf(" (name-based)\n"); + break; + case 4: + printf(" (random)\n"); + break; + default: + printf("\n"); + } + if (type != 1) { + printf("Warning: not a time-based UUID, so UUID time " + "decoding will likely not work!\n"); + } + printf("UUID time is: (%ld, %ld): %s\n", tv.tv_sec, tv.tv_usec, + ctime(&time_reg)); + + return 0; +} +#endif diff --git a/cpukit/libmisc/uuid/uuidd.h b/cpukit/libmisc/uuid/uuidd.h new file mode 100644 index 0000000000..528acdc9d0 --- /dev/null +++ b/cpukit/libmisc/uuid/uuidd.h @@ -0,0 +1,53 @@ +/* + * Definitions used by the uuidd daemon + * + * Copyright (C) 2007 Theodore Ts'o. + * + * %Begin-Header% + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifndef _UUID_UUIDD_H +#define _UUID_UUIDD_H + +#define UUIDD_SOCKET_PATH "/var/lib/libuuid/request" +#define UUIDD_PIDFILE_PATH "/var/lib/libuuid/uuidd.pid" +#define UUIDD_PATH "/usr/sbin/uuidd" + +#define UUIDD_OP_GETPID 0 +#define UUIDD_OP_GET_MAXOP 1 +#define UUIDD_OP_TIME_UUID 2 +#define UUIDD_OP_RANDOM_UUID 3 +#define UUIDD_OP_BULK_TIME_UUID 4 +#define UUIDD_OP_BULK_RANDOM_UUID 5 +#define UUIDD_MAX_OP UUIDD_OP_BULK_RANDOM_UUID + +extern void uuid__generate_time(uuid_t out, int *num); +extern void uuid__generate_random(uuid_t out, int *num); + +#endif /* _UUID_UUID_H */ |