Command-Line Redirection

MicroMonitor also includes support for command-line redirection. Command-line redirection is handy, for example, to log the output of a command to a file. My primary motivation for adding command-line redirection is to support the ability to dump a stack trace output to a file.

I discussed exception handling earlier. In the next few chapters, I will describe a flash file system and show how to dump an application stack. Consider the case where your system is in the field and taking a strange exception every other blue moon. How do you find the cause for the strange exception without camping out at the customer site? If the exception handler has the optional ability to execute a specified script (from the file system), then that script, among other things, can issue a stack trace command ( strace ) and redirect the output to a file. If this script has been activated, when the customer calls saying that something appears to have crashed and restarted, you can query the file system for the trace output file and see what caused the exception.

By default all printf output passes through putchar () , which sends each character to the console port. If putchar has some additional smarts and can place its incoming character into a buffer of some known size , then the output will be logged to RAM. To convert this internal logging to output redirection, I need only provide a mechanism to tell MicroMonitor to transfer the buffer to a file. Traditionally, CLI redirection syntax is

 echo hey you >filename 

This syntax isnt good for MicroMonitors command-line redirection because I want the output to go to RAM first. So I deviate slightly with two different forms:

  1. echo hey you > buffer, buffer_size[, filename]

    This format consists of single right arrow with a one- or two-comma delimited string containing a buffer address followed by the size of the buffer and an optional filename. If the filename is specified, the output of the command is copied to the buffer (truncated at buffer_size if necessary) and then transferred to TFS as filename . The running buffer pointer is reset back to the base address of buffer . If a filename is omitted, the output of the command is copied to the buffer, and the pointer into the buffer is left at the position following the data ( assuming buffer_size is not reached).

  2. echo hey you again >>[filename]

    This syntax is used to append the output of the command to the buffer that was created by the > syntax described above. If a filename is included, the content of the buffer is transferred to TFS under the specified name (in this example), and the running buffer pointer is reset to the base address of the buffer. If a filename is not specified, there is no transfer to a file, and the running pointer is incremented by one to the position just after the data copied (once again, assuming buffer_size is not reached).

To implement this feature, the function putchar() needs an additional call at the top (see Listing 5.7).

Listing 5.7: Adding Redirection to putchar().
image from book
 int putchar(uchar c) {     RedirectCharacter(c);     if (c == '\n')         rputchar('\r');     rputchar(c);     return((int)c); } 
image from book
 

The RedirectCharacter() function (see Listing 5.8) looks at a state to determine if it does anything at all. The RedirectCharacter() function either does nothing or, after checking to see that the running buffer pointer has not reached the end of the buffer, copies the character to the buffer space and increments the pointer by one.

Listing 5.8: RedirectCharacter().
image from book
 void RedirectCharacter(char c) {     if (RedirectState == REDIRECT_ACTIVE) {         if (RedirectPtr < RedirectEnd)             *RedirectPtr++ = c;     } } 
image from book
 

I must also modify docommand() so it can recognize the redirection syntax (set RedirectState to REDIRECT_ACTIVE ) and, when appropriate, notify the redirection code that the command has completed (set RedirectState back to REDIRECT_IDLE ). So I must add a few lines to docommand() .

 if (RedirectionCheck(cmdcpy) == -1)     return(CMD_LINE_ERROR); 

I insert the preceding code into docommand() just after the shell variable processing. Placing the modification after shell variable processing allows the user to do something like

 echo hi >$APPRAMBASE,100 

and know that when RedirectionCheck() is called, the content of $APPRAMBASE has already been converted to a physical address by the CLI shell-variable processor described previously. At the bottom of docommand() , I add another call to indicate command completion. This call is simply a call to RedirectionDone() prior to returning from docommand() .

The RedirectionCheck() function (see Listing 5.9) starts by looking through the line for the redirection symbol. If, at the end of the line, RedirectionCheck() hasnt found a right arrow ( > ), it simply returns. If it finds a second arrow, then RedirectState should already be REDIRECT_ACTIVE; any other state indicates an error. If RedirectState is correct, then RedirectionCheck() looks for a filename and stores it away for use later. Finally, when RedirectionCheck() does not find a second arrow, it processes the comma-delimited string after the arrow and establishes pointers and counters appropriately.

Listing 5.9: Parsing Redirection Commands.
image from book
 #define REDIRECT_UNINITIALIZED  0 #define REDIRECT_ACTIVE         0x12345678 #define REDIRECT_IDLE           0x87654321 static int  RedirectSize, RedirectState; static char *RedirectBase, *RedirectPtr, *RedirectEnd; static char RedirectFile[TFSNAMESIZE]; int RedirectionCheck(char *cmdcpy) {     int inquote;     char *arrow, *base, *comma, *space;     base = cmdcpy;     arrow = (char *)0;     /* Parse the incoming command line looking for a right arrow.      * This parsing assumes that there will be no negated arrows      * (preceding backslash) after a non-negated arrow is detected.      * Note that a redirection arrow within a double-quote set is      * ignored.  This allows a shell variable that contains a right arrow      * to be printed properly if put in double quotes.      *  For example...      *  set PROMPT "maint> "      *  echo $PROMPT    # This will generate a redirection syntax error      *      echo "$PROMPT"  # This won't.      */     inquote = 0;     while(*cmdcpy) {         if ((*cmdcpy == '"') && ((cmdcpy == base)  (*(cmdcpy-1) != '\'))) {             inquote = inquote == 1 ? 0 : 1;             cmdcpy++;             continue;         }            if (inquote == 1) {             cmdcpy++;             continue;         }         if (*cmdcpy == '>') {             arrow = cmdcpy;             if (*(arrow-1) == '\') {                 strcpy(arrow-1,arrow);                 cmdcpy = arrow+1;                 arrow = (char *)0;                 continue;             }             break;         }         cmdcpy++;     }     if (arrow == (char *)0)         return(0);     /* Remove the remaining text from the command line because it is to      * be used only by the redirection mechanism.      */     *arrow = 0;     /* Now parse the text after the first non-negated arrow. */     if (*(arrow+1) == '>') {         if (RedirectState == REDIRECT_UNINITIALIZED) {             printf("Redirection not initialized\n");             return(-1);         }         arrow += 2;         while(isspace(*arrow))             arrow++;         if (*arrow != 0)             strncpy(RedirectFile,arrow,TFSNAMESIZE);     }     else {         RedirectPtr = RedirectBase = (char *)strtoul(arrow+1,&comma,0);         if (*comma == ',') {             RedirectSize = (int)strtol(comma+1,&comma,0);             if (RedirectSize <= 0) {                 printf("Redirection size error: %d\n",RedirectSize);                 return(-1);             }             RedirectEnd = RedirectBase + RedirectSize;             if (*comma == ',') {                 space = strpbrk(comma," \t\r\n");                 if (space)                     *space = 0;                 strncpy(RedirectFile,comma+1,TFSNAMESIZE);             }             else                 RedirectFile[0] = 0;         }         else {             printf("Redirection syntax error\n");             return(-1);         }     }     RedirectState = REDIRECT_ACTIVE;     return(0); } 
image from book
 

Finally, docommand() informs the redirection code that the command completed by calling RedirectionCmdDone() (see Listing 5.10).

Listing 5.10: RedirectionCmdDone().
image from book
 void RedirectionCmdDone(void) {     if (RedirectState != REDIRECT_UNINITIALIZED) {         RedirectState = REDIRECT_IDLE;         if (RedirectFile[0]) {             tfsadd(RedirectFile,0,0,(uchar *)RedirectBase,                 (int)(RedirectPtr-RedirectBase));             RedirectFile[0] = 0;             RedirectPtr = RedirectBase;         }     } } 
image from book
 

If the state has been previously initialized (by RedirectionCheck() ) this function checks for the presence of the redirection filename. The redirection filename is only present if the filename was specified as part of the command. Assuming the redirection filename is set, the buffer is transferred to a file using the flash file system call tfsadd() , which simply transfers a block of memory to a file in the flash file system.

Although in some implementations redirection can be a complicated add-on, here the whole facility is really trivial to incorporate into the CLI. Notice that the only complexity added to docommand() are calls to RedirectionCheck() and RedirectionCmdDone() . This implementation of command-line redirection is a good example of modular functionality. Complexity for the feature is limited to the function responsible for the complexity; the complexity is not distributed throughout other components .



Embedded Systems Firmware Demystified
Embedded Systems Firmware Demystified (With CD-ROM)
ISBN: 1578200997
EAN: 2147483647
Year: 2002
Pages: 118
Authors: Ed Sutter

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net