source: rtems/cpukit/libmisc/monitor/mon-editor.c @ bec8f2b

4.10
Last change on this file since bec8f2b was bec8f2b, checked in by Joel Sherrill <joel.sherrill@…>, on 06/21/10 at 16:25:20

2010-06-21 Joel Sherrill <joel.sherrilL@…>

PR 1559/misc
Coverity Id 16

  • libmisc/monitor/mon-editor.c: Fix buffer overflow.
  • Property mode set to 100644
File size: 16.0 KB
Line 
1/**
2 * @file
3 *
4 * @brief Command line editor for RTEMS monitor.
5 */
6
7/*
8 * $Id$
9 *
10 * 2001-01-30 KJO (vac4050@cae597.rsc.raytheon.com):
11 *  Fixed rtems_monitor_command_lookup() to accept partial
12 *  commands to uniqeness.  Added support for setting
13 *  the monitor prompt via an environment variable:
14 *  RTEMS_MONITOR_PROMPT
15 *
16 * CCJ: 26-3-2000, adding command history and command line
17 * editing. This code is donated from My Right Boot and not
18 * covered by GPL, only the RTEMS license.
19 */
20
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
24
25#include <rtems.h>
26
27#include <rtems/monitor.h>
28
29#include <stdio.h>
30#include <string.h>
31#include <stdlib.h>
32#include <inttypes.h>
33#include <termios.h>
34#include <unistd.h>
35
36#ifndef MONITOR_PROMPT
37#define MONITOR_PROMPT "rtems"          /* will have '> ' appended */
38#endif
39
40/*
41 * Some key labels to define special keys.
42 */
43
44#define KEYS_EXTENDED    (0x8000)
45#define KEYS_NORMAL_MASK (0x00ff)
46#define KEYS_INS         (0)
47#define KEYS_DEL         (1)
48#define KEYS_UARROW      (2)
49#define KEYS_DARROW      (3)
50#define KEYS_LARROW      (4)
51#define KEYS_RARROW      (5)
52#define KEYS_HOME        (6)
53#define KEYS_END         (7)
54#define KEYS_F1          (8)
55#define KEYS_F2          (9)
56#define KEYS_F3          (10)
57#define KEYS_F4          (11)
58#define KEYS_F5          (12)
59#define KEYS_F6          (13)
60#define KEYS_F7          (14)
61#define KEYS_F8          (15)
62#define KEYS_F9          (16)
63#define KEYS_F10         (17)
64
65#define RTEMS_COMMAND_BUFFER_SIZE (75)
66
67static char monitor_prompt[32];
68static char buffer[RTEMS_COMMAND_BUFFER_SIZE];
69static int  pos;
70static int  logged_in;
71
72/*
73 * History data.
74 */
75
76#define RTEMS_COMMAND_HISTORIES (20)
77
78static char history_buffer[RTEMS_COMMAND_HISTORIES][RTEMS_COMMAND_BUFFER_SIZE];
79static int  history_pos[RTEMS_COMMAND_HISTORIES];
80static int  history;
81static int  history_next;
82
83/*
84 * Translation tables. Not sure if this is the best way to
85 * handle this, how-ever I wish to avoid the overhead of
86 * including a more complete and standard environment such
87 * as ncurses.
88 */
89
90struct translation_table
91{
92  char                     expecting;
93  const struct translation_table *branch;
94  unsigned int             key;
95};
96
97static const struct translation_table trans_two[] =
98{
99  { '~', 0, KEYS_INS },
100  { 0,   0, 0 }
101};
102
103static const struct translation_table trans_three[] =
104{
105  { '~', 0, KEYS_DEL },
106  { 0,   0, 0 }
107};
108
109static const struct translation_table trans_tab_csi[] =
110{
111  { '2', trans_two,   0 },
112  { '3', trans_three, 0 },
113  { 'A', 0,           KEYS_UARROW },
114  { 'B', 0,           KEYS_DARROW },
115  { 'D', 0,           KEYS_LARROW },
116  { 'C', 0,           KEYS_RARROW },
117  { 'F', 0,           KEYS_END },
118  { 'H', 0,           KEYS_HOME },
119  { 0,   0,           0 }
120};
121
122static const struct translation_table trans_tab_O[] =
123{
124  { '1', 0, KEYS_F1 },
125  { '2', 0, KEYS_F2 },
126  { '3', 0, KEYS_F3 },
127  { '4', 0, KEYS_F4 },
128  { '5', 0, KEYS_F5 },
129  { '6', 0, KEYS_F6 },
130  { '7', 0, KEYS_F7 },
131  { '8', 0, KEYS_F8 },
132  { '9', 0, KEYS_F9 },
133  { ':', 0, KEYS_F10 },
134  { 'P', 0, KEYS_F1 },
135  { 'Q', 0, KEYS_F2 },
136  { 'R', 0, KEYS_F3 },
137  { 'S', 0, KEYS_F4 },
138  { 'T', 0, KEYS_F5 },
139  { 'U', 0, KEYS_F6 },
140  { 'V', 0, KEYS_F7 },
141  { 'W', 0, KEYS_F8 },
142  { 'X', 0, KEYS_F9 },
143  { 'Y', 0, KEYS_F10 },
144  { 0,   0, 0 }
145};
146
147static const struct translation_table trans_tab[] =
148{
149  { '[', trans_tab_csi, 0 },    /* CSI command sequences */
150  { 'O', trans_tab_O,   0 },    /* O are the fuction keys */
151  { 0,   0,             0 }
152};
153
154/*
155 * Perform a basic translation for some ANSI/VT100 key codes.
156 * This code could do with a timeout on the ESC as it is
157 * now lost from the input stream. It is not* used by the
158 * line editor below so considiered not worth the effort.
159 */
160
161static unsigned int
162rtems_monitor_getchar (void)
163{
164  const struct translation_table *translation = 0;
165  for (;;)
166  {
167    char c = getchar ();
168    if (c == 27)
169      translation = trans_tab;
170    else
171    {
172      /*
173       * If no translation happing just pass through
174       * and return the key.
175       */
176      if (translation)
177      {
178        /*
179         * Scan the current table for the key, and if found
180         * see if this key is a fork. If so follow it and
181         * wait else return the extended key.
182         */
183        int index    = 0;
184        int branched = 0;
185        while ((translation[index].expecting != '\0') ||
186               (translation[index].key != '\0'))
187        {
188          if (translation[index].expecting == c)
189          {
190            /*
191             * A branch is take if more keys are to come.
192             */
193            if (translation[index].branch == 0)
194              return KEYS_EXTENDED | translation[index].key;
195            else
196            {
197              translation = translation[index].branch;
198              branched    = 1;
199              break;
200            }
201          }
202          index++;
203        }
204        /*
205         * Who knows what these keys are, just drop them.
206         */
207        if (!branched)
208          translation = 0;
209      }
210      else
211        return c;
212    }
213  }
214}
215
216/*
217 * The line editor with history.
218 */
219
220static int
221rtems_monitor_line_editor (
222    char *command
223)
224{
225  int repeating = 0;
226
227  memset (buffer, 0, RTEMS_COMMAND_BUFFER_SIZE);
228  history = history_next;
229  pos     = 0;
230
231  if (!logged_in)
232    fprintf(stdout,"\nMonitor ready, press enter to login.\n\n");
233  else
234    fprintf(stdout,"%s $ ", monitor_prompt);
235
236  while (1)
237  {
238    unsigned int extended_key;
239    char         c;
240
241    fflush (stdout);
242
243    extended_key = rtems_monitor_getchar ();
244    c = extended_key & KEYS_NORMAL_MASK;
245
246    /*
247     * Make the extended_key usable as a boolean.
248     */
249    extended_key &= ~KEYS_NORMAL_MASK;
250
251    if (!extended_key && !logged_in)
252    {
253      if (c == '\n')
254      {
255        logged_in = 1;
256        /*
257         * The prompt has changed from `>' to `$' to help know
258         * which version of the monitor code people are using.
259         */
260        fprintf(stdout,"%s $ ", monitor_prompt);
261      }
262    }
263    else
264    {
265      if (extended_key)
266      {
267        switch (c)
268        {
269          case KEYS_END:
270            fprintf(stdout,buffer + pos);
271            pos = (int) strlen (buffer);
272            break;
273
274          case KEYS_HOME:
275            fprintf(stdout,"\r%s $ ", monitor_prompt);
276            pos = 0;
277            break;
278
279          case KEYS_LARROW:
280            if (pos > 0)
281            {
282              pos--;
283              putchar ('\b');
284            }
285            break;
286
287          case KEYS_RARROW:
288            if ((pos < RTEMS_COMMAND_BUFFER_SIZE) && (buffer[pos] != '\0'))
289            {
290              putchar (buffer[pos]);
291              pos++;
292            }
293            break;
294
295          case KEYS_UARROW:
296            /*
297             * If we are moving up the histories then we need to save the working
298             * buffer.
299             */
300            if (history)
301            {
302              int end;
303              int bs;
304              if (history == history_next)
305              {
306                memcpy (history_buffer[history_next], buffer,
307                        RTEMS_COMMAND_BUFFER_SIZE);
308                history_pos[history_next] = pos;
309              }
310              history--;
311              memcpy (buffer, history_buffer[history],
312                      RTEMS_COMMAND_BUFFER_SIZE);
313              pos = history_pos[history];
314              fprintf(stdout,"\r%*c", RTEMS_COMMAND_BUFFER_SIZE, ' ');
315              fprintf(stdout,"\r%s $ %s", monitor_prompt, buffer);
316              end = (int) strlen (buffer);
317              for (bs = 0; bs < (end - pos); bs++)
318                putchar ('\b');
319            }
320            break;
321
322          case KEYS_DARROW:
323            if (history < history_next)
324            {
325              int end;
326              int bs;
327              history++;
328              memcpy (buffer, history_buffer[history],
329                      RTEMS_COMMAND_BUFFER_SIZE);
330              pos = history_pos[history];
331              fprintf(stdout,"\r%*c", RTEMS_COMMAND_BUFFER_SIZE, ' ');
332              fprintf(stdout,"\r%s $ %s", monitor_prompt, buffer);
333              end = (int) strlen (buffer);
334              for (bs = 0; bs < (end - pos); bs++)
335                putchar ('\b');
336            }
337            break;
338
339          case KEYS_DEL:
340            if (buffer[pos] != '\0')
341            {
342              int end;
343              int bs;
344              strcpy (&buffer[pos], &buffer[pos + 1]);
345              fprintf(stdout,"\r%s $ %s", monitor_prompt, buffer);
346              end = (int) strlen (buffer);
347              for (bs = 0; bs < (end - pos); bs++)
348                putchar ('\b');
349            }
350            break;
351        }
352      }
353      else
354      {
355        switch (c)
356        {
357          case '\b':
358          case '\x7e':
359          case '\x7f':
360            if (pos > 0)
361            {
362              int bs;
363              pos--;
364              strcpy (buffer + pos, buffer + pos + 1);
365              fprintf(stdout,"\b%s \b", buffer + pos);
366              for (bs = 0; bs < ((int) strlen (buffer) - pos); bs++)
367                putchar ('\b');
368            }
369            break;
370
371          case '\n':
372            /*
373             * Process the command.
374             */
375            fprintf(stdout,"\n");
376            repeating = 1;
377            /*
378             * Only process the history if we have a command and
379             *a history.
380             */
381            if (strlen (buffer))
382            {
383              if (history_next && (history == history_next))
384              {
385                /*
386                 * Do not place the last command into the history
387                 *if the same.
388                 */
389                if (strcmp (history_buffer[history_next - 1], buffer))
390                  repeating = 0;
391              }
392              else
393                repeating = 0;
394            }
395            if (!repeating)
396            {
397              memcpy (history_buffer[history_next], buffer,
398                      RTEMS_COMMAND_BUFFER_SIZE);
399              history_pos[history_next] = pos;
400              if (history_next < (RTEMS_COMMAND_HISTORIES - 1))
401                history_next++;
402              else
403              {
404                memmove (history_buffer[0], history_buffer[1],
405                         RTEMS_COMMAND_BUFFER_SIZE * (RTEMS_COMMAND_HISTORIES - 1));
406                memmove (&history_pos[0], &history_pos[1],
407                         sizeof (history_pos[0]) * (RTEMS_COMMAND_HISTORIES - 1));
408              }
409            }
410            else
411            {
412#ifdef ENABLE_ENTER_REPEATS
413              if (history_next)
414                memcpy (buffer, history_buffer[history_next - 1],
415                        RTEMS_COMMAND_BUFFER_SIZE);
416#endif
417            }
418            memmove (command, buffer, RTEMS_COMMAND_BUFFER_SIZE);
419            return repeating;
420            break;
421
422          default:
423            if ((pos < (RTEMS_COMMAND_BUFFER_SIZE - 1)) &&
424                (c >= ' ') && (c <= 'z'))
425            {
426              int end;
427              end = strlen (buffer);
428              if ((pos < end) && (end < RTEMS_COMMAND_BUFFER_SIZE))
429              {
430                int ch, bs;
431                for (ch = end; ch > pos; ch--)
432                  buffer[ch] = buffer[ch - 1];
433                fprintf(stdout,buffer + pos);
434                for (bs = 0; bs < (end - pos + 1); bs++)
435                  putchar ('\b');
436              }
437              buffer[pos++] = c;
438              if (pos > end)
439                buffer[pos] = '\0';
440              putchar (c);
441            }
442            break;
443        }
444      }
445    }
446  }
447}
448
449/*
450 * make_argv(cp): token-count
451 *  Break up the command line in 'cp' into global argv[] and argc (return
452 *  value).
453 */
454
455int
456rtems_monitor_make_argv(
457    char *cp,
458    int  *argc_p,
459    char **argv)
460{
461  int argc = 0;
462
463  while ((cp = strtok(cp, " \t\n\r")))
464  {
465    argv[argc++] = cp;
466    cp = (char *) NULL;
467  }
468  argv[argc] = (char *) NULL;      /* end of argv */
469
470  return *argc_p = argc;
471}
472
473
474/*
475 * Read and break up a monitor command
476 *
477 * We have to loop on the gets call, since it will return NULL under UNIX
478 *  RTEMS when we get a signal (eg: SIGALRM).
479 */
480
481int
482rtems_monitor_command_read(char *command,
483                           int  *argc,
484                           char **argv)
485{
486        char *env_prompt;
487
488        env_prompt = getenv("RTEMS_MONITOR_PROMPT");
489
490  /*
491   * put node number in the prompt if we are multiprocessing
492   */
493#if defined(RTEMS_MULTIPROCESSING)
494  if (!rtems_configuration_get_user_multiprocessing_table ())
495    sprintf (monitor_prompt, "%s",
496             (env_prompt == NULL) ? MONITOR_PROMPT: env_prompt);
497  else /* .... */
498#endif
499  if (rtems_monitor_default_node != rtems_monitor_node)
500    sprintf (monitor_prompt, "%" PRId32 "-%s-%" PRId32 "", rtems_monitor_node,
501             (env_prompt == NULL) ? MONITOR_PROMPT : env_prompt,
502             rtems_monitor_default_node);
503  else
504    sprintf (monitor_prompt, "%" PRId32 "-%s", rtems_monitor_node,
505             (env_prompt == NULL) ? MONITOR_PROMPT : env_prompt);
506
507  rtems_monitor_line_editor (command);
508
509  return rtems_monitor_make_argv (command, argc, argv);
510}
511
512/*
513 * Main monitor command loop
514 */
515
516void
517rtems_monitor_task(
518    rtems_task_argument monitor_flags
519)
520{
521    rtems_tcb *debugee = 0;
522    rtems_context *rp;
523#if (CPU_HARDWARE_FP == TRUE) || (CPU_SOFTWARE_FP == TRUE)
524    rtems_context_fp *fp;
525#endif
526    char command_buffer[513];
527    int argc;
528    char *argv[64];
529    bool  verbose = false;
530    struct termios term;
531
532    /*
533     * Make the stdin stream characte not line based.
534     */
535
536    if (tcgetattr (STDIN_FILENO, &term) < 0)
537    {
538      fprintf(stdout,"rtems-monitor: cannot get terminal attributes.\n");
539    }
540    else
541    {
542      /*
543       * No echo, no canonical processing.
544       */
545
546      term.c_lflag &= ~(ECHO | ICANON | IEXTEN);
547
548      /*
549       * No sigint on BREAK, CR-to-NL off, input parity off,
550       * don't strip 8th bit on input, output flow control off
551       */
552
553      term.c_lflag    &= ~(INPCK | ISTRIP | IXON);
554      term.c_cc[VMIN]  = 1;
555      term.c_cc[VTIME] = 0;
556
557      if (tcsetattr (STDIN_FILENO, TCSANOW, &term) < 0)
558      {
559        fprintf(stdout,"cannot set terminal attributes\n");
560      }
561    }
562
563    if (!(monitor_flags & RTEMS_MONITOR_NOSYMLOAD)) {
564      rtems_monitor_symbols_loadup();
565    }
566
567    if (monitor_flags & RTEMS_MONITOR_SUSPEND)
568        (void) rtems_monitor_suspend(RTEMS_NO_TIMEOUT);
569
570    for (;;)
571    {
572        const rtems_monitor_command_entry_t *command;
573
574        debugee = _Thread_Executing;
575        rp = &debugee->Registers;
576#if (CPU_HARDWARE_FP == TRUE) || (CPU_SOFTWARE_FP == TRUE)
577        fp = debugee->fp_context;  /* possibly 0 */
578#endif
579
580        if (0 == rtems_monitor_command_read(command_buffer, &argc, argv))
581            continue;
582        if (argc < 1
583          || (command = rtems_monitor_command_lookup(argv [0])) == 0) {
584          /* no command */
585          fprintf(stdout,"Unrecognised command; try 'help'\n");
586          continue;
587        }
588
589        command->command_function(argc, argv, &command->command_arg, verbose);
590
591        fflush(stdout);
592    }
593}
594
595
596void
597rtems_monitor_kill(void)
598{
599    if (rtems_monitor_task_id)
600        rtems_task_delete(rtems_monitor_task_id);
601    rtems_monitor_task_id = 0;
602
603    rtems_monitor_server_kill();
604}
605
606void
607rtems_monitor_init(
608    uint32_t   monitor_flags
609)
610{
611    rtems_status_code status;
612
613    rtems_monitor_kill();
614
615    status = rtems_task_create(RTEMS_MONITOR_NAME,
616                               1,
617                               RTEMS_MINIMUM_STACK_SIZE * 2,
618                               RTEMS_INTERRUPT_LEVEL(0),
619                               RTEMS_DEFAULT_ATTRIBUTES,
620                               &rtems_monitor_task_id);
621    if (status != RTEMS_SUCCESSFUL)
622    {
623        rtems_error(status, "could not create monitor task");
624        return;
625    }
626
627    rtems_monitor_node = rtems_object_id_get_node(rtems_monitor_task_id);
628    rtems_monitor_default_node = rtems_monitor_node;
629
630    rtems_monitor_server_init(monitor_flags);
631
632    if (!(monitor_flags & RTEMS_MONITOR_NOTASK)) {
633      /*
634       * Start the monitor task itself
635       */
636      status = rtems_task_start(
637        rtems_monitor_task_id, rtems_monitor_task, monitor_flags);
638      if (status != RTEMS_SUCCESSFUL) {
639        rtems_error(status, "could not start monitor");
640        return;
641      }
642   }
643}
Note: See TracBrowser for help on using the repository browser.