source: rtems/cpukit/libmisc/shell/shell.c @ 373ccbb9

4.115
Last change on this file since 373ccbb9 was 373ccbb9, checked in by Sebastian Huber <sebastian.huber@…>, on 11/17/14 at 13:55:38

shell: Use exiting once initialization

Avoid TOCTOU issues. Avoid pull in of global buffers.

  • Property mode set to 100644
File size: 28.1 KB
Line 
1/*
2 *
3 *  Instantatiate a new terminal shell.
4 *
5 *  Author:
6 *
7 *   WORK: fernando.ruiz@ctv.es
8 *   HOME: correo@fernando-ruiz.com
9 *
10 *  The license and distribution terms for this file may be
11 *  found in the file LICENSE in this distribution or at
12 *  http://www.rtems.org/license/LICENSE.
13 */
14
15#ifdef HAVE_CONFIG_H
16#include "config.h"
17#endif
18
19#include <stdio.h>
20#include <time.h>
21
22#include <rtems.h>
23#include <rtems/error.h>
24#include <rtems/libio.h>
25#include <rtems/libio_.h>
26#include <rtems/system.h>
27#include <rtems/shell.h>
28#include <rtems/shellconfig.h>
29#include <rtems/console.h>
30#include "internal.h"
31
32#include <termios.h>
33#include <string.h>
34#include <stdlib.h>
35#include <ctype.h>
36#include <sys/stat.h>
37#include <unistd.h>
38#include <errno.h>
39#include <pwd.h>
40#include <pthread.h>
41#include <assert.h>
42
43const rtems_shell_env_t rtems_global_shell_env = {
44  .magic         = rtems_build_name('S', 'E', 'N', 'V'),
45  .devname       = CONSOLE_DEVICE_NAME,
46  .taskname      = "SHGL",
47  .exit_shell    = false,
48  .forever       = true,
49  .errorlevel    = -1,
50  .echo          = false,
51  .cwd           = "/",
52  .input         = NULL,
53  .output        = NULL,
54  .output_append = false,
55  .wake_on_end   = RTEMS_ID_NONE,
56  .login_check   = NULL
57};
58
59static pthread_once_t rtems_shell_once = PTHREAD_ONCE_INIT;
60
61static pthread_key_t rtems_shell_current_env_key;
62
63/*
64 *  Initialize the shell user/process environment information
65 */
66static rtems_shell_env_t *rtems_shell_init_env(
67  rtems_shell_env_t *shell_env_p
68)
69{
70  rtems_shell_env_t *shell_env;
71
72  shell_env = malloc(sizeof(rtems_shell_env_t));
73  if ( !shell_env )
74    return NULL;
75  if ( !shell_env_p ) {
76    *shell_env = rtems_global_shell_env;
77  } else {
78    *shell_env = *shell_env_p;
79  }
80  shell_env->taskname = NULL;
81
82  return shell_env;
83}
84
85/*
86 *  Completely free a shell_env_t and all associated memory
87 */
88static void rtems_shell_env_free(
89  void *ptr
90)
91{
92  rtems_shell_env_t *shell_env;
93  shell_env = (rtems_shell_env_t *) ptr;
94
95  if ( !ptr )
96    return;
97
98  if ( shell_env->input )
99    free((void *)shell_env->input);
100  if ( shell_env->output )
101    free((void *)shell_env->output);
102  free( ptr );
103}
104
105static void rtems_shell_create_file(const char *name, const char *content)
106{
107  FILE *fp = fopen(name, "wx");
108
109  if (fp != NULL) {
110    fputs(content, fp);
111    fclose(fp);
112  }
113}
114
115static void rtems_shell_init_once(void)
116{
117  struct passwd pwd;
118  struct passwd *pwd_res;
119
120  pthread_key_create(&rtems_shell_current_env_key, rtems_shell_env_free);
121
122  /* dummy call to init /etc dir */
123  getpwuid_r(0, &pwd, NULL, 0, &pwd_res);
124
125  rtems_shell_create_file("etc/issue",
126                          "\n"
127                          "Welcome to @V\\n"
128                          "Login into @S\\n");
129
130  rtems_shell_create_file("/etc/issue.net",
131                          "\n"
132                          "Welcome to %v\n"
133                          "running on %m\n");
134}
135
136/*
137 *  Return the current shell environment
138 */
139rtems_shell_env_t *rtems_shell_get_current_env(void)
140{
141  return (rtems_shell_env_t *) pthread_getspecific(rtems_shell_current_env_key);
142}
143
144/*
145 *  Duplication the current shell environment and if none is set
146 *  clear it.
147 */
148void rtems_shell_dup_current_env(rtems_shell_env_t *copy)
149{
150  rtems_shell_env_t *env = rtems_shell_get_current_env();
151  if (env) {
152    *copy = *env;
153  }
154  else {
155    memset(copy, 0, sizeof(rtems_shell_env_t));
156    copy->magic    = rtems_build_name('S', 'E', 'N', 'V');
157    copy->devname  = CONSOLE_DEVICE_NAME;
158    copy->taskname = "RTSH";
159  }
160}
161
162/*
163 *  Get a line of user input with modest features
164 */
165static int rtems_shell_line_editor(
166  char       *cmds[],
167  int         count,
168  int         size,
169  const char *prompt,
170  FILE       *in,
171  FILE       *out
172)
173{
174  unsigned int extended_key;
175  int          c;
176  int          col;
177  int          last_col;
178  int          output;
179  char         line[size];
180  char         new_line[size];
181  int          up;
182  int          cmd = -1;
183  int          inserting = 1;
184
185  output = (out && isatty(fileno(in)));
186
187  col = last_col = 0;
188
189  tcdrain(fileno(in));
190  if (out)
191    tcdrain(fileno(out));
192
193  if (output && prompt)
194    fprintf(out, "\r%s", prompt);
195
196  line[0] = 0;
197  new_line[0] = 0;
198
199  for (;;) {
200
201    if (output)
202      fflush(out);
203
204    extended_key = rtems_shell_getchar(in);
205
206    if (extended_key == EOF)
207      return -2;
208
209    c = extended_key & RTEMS_SHELL_KEYS_NORMAL_MASK;
210
211    /*
212     * Make the extended_key usable as a boolean.
213     */
214    extended_key &= ~RTEMS_SHELL_KEYS_NORMAL_MASK;
215
216    up = 0;
217
218    if (extended_key)
219    {
220      switch (c)
221      {
222        case RTEMS_SHELL_KEYS_END:
223          if (output)
224            fprintf(out, "%s", line + col);
225          col = (int) strlen (line);
226          break;
227
228        case RTEMS_SHELL_KEYS_HOME:
229          if (output) {
230            if (prompt)
231              fprintf(out,"\r%s", prompt);
232          }
233          col = 0;
234          break;
235
236        case RTEMS_SHELL_KEYS_LARROW:
237          c = 2;
238          extended_key = 0;
239          break;
240
241        case RTEMS_SHELL_KEYS_RARROW:
242          c = 6;
243          extended_key = 0;
244          break;
245
246        case RTEMS_SHELL_KEYS_UARROW:
247          c = 16;
248          extended_key = 0;
249          break;
250
251        case RTEMS_SHELL_KEYS_DARROW:
252          c = 14;
253          extended_key = 0;
254          break;
255
256        case RTEMS_SHELL_KEYS_DEL:
257          if (line[col] != '\0')
258          {
259            int end;
260            int bs;
261            strcpy (&line[col], &line[col + 1]);
262            if (output) {
263              fprintf(out,"\r%s%s ", prompt, line);
264              end = (int) strlen (line);
265              for (bs = 0; bs < ((end - col) + 1); bs++)
266                fputc('\b', out);
267            }
268          }
269          break;
270
271        case RTEMS_SHELL_KEYS_INS:
272          inserting = inserting ? 0 : 1;
273          break;
274      }
275    }
276    if (!extended_key)
277    {
278      switch (c)
279      {
280        case 1:                         /*Control-a*/
281          if (output) {
282            if (prompt)
283              fprintf(out,"\r%s", prompt);
284          }
285          col = 0;
286          break;
287
288        case 2:                         /* Control-B */
289          if (col > 0)
290          {
291            col--;
292            if (output)
293              fputc('\b', out);
294          }
295          break;
296
297        case 4:                         /* Control-D */
298          if (strlen(line)) {
299            if (col < strlen(line)) {
300              strcpy (line + col, line + col + 1);
301              if (output) {
302                int bs;
303                fprintf(out,"%s \b", line + col);
304                for (bs = 0; bs < ((int) strlen (line) - col); bs++)
305                  fputc('\b', out);
306              }
307            }
308            break;
309          }
310          /* Fall through */
311
312        case EOF:
313          if (output)
314            fputc('\n', out);
315          return -2;
316
317        case 5:                         /*Control-e*/
318          if (output)
319            fprintf(out, "%s", line + col);
320          col = (int) strlen (line);
321          break;
322
323        case 6:                         /* Control-F */
324          if ((col < size) && (line[col] != '\0')) {
325            if (output)
326              fputc(line[col], out);
327            col++;
328          }
329          break;
330
331        case 7:                         /* Control-G */
332          if (output) {
333            /*
334             * The (int) cast is needed because the width specifier (%*)
335             * must be an int, but strlen() returns a size_t. Without
336             * the case, the result is a printf() format warning.
337             */
338            fprintf(out,"\r%s%*c", prompt, (int) strlen (line), ' ');
339            fprintf(out,"\r%s\x7", prompt);
340          }
341          memset (line, '\0', strlen(line));
342          col = 0;
343          break;
344
345        case 11:                        /*Control-k*/
346          if (line[col]) {
347            if (output) {
348              int end = strlen(line);
349              int bs;
350              fprintf(out,"%*c", end - col, ' ');
351              for (bs = 0; bs < (end - col); bs++)
352                fputc('\b', out);
353            }
354            line[col] = '\0';
355          }
356          break;
357
358        case '\f':
359          if (output) {
360            int end;
361            int bs;
362            fputc('\f',out);
363            fprintf(out,"\r%s%s", prompt, line);
364            end = (int) strlen (line);
365            for (bs = 0; bs < (end - col); bs++)
366              fputc('\b', out);
367          }
368          break;
369
370        case '\b':
371        case '\x7f':
372          if (col > 0)
373          {
374            int bs;
375            col--;
376            strcpy (line + col, line + col + 1);
377            if (output) {
378              fprintf(out,"\b%s \b", line + col);
379              for (bs = 0; bs < ((int) strlen (line) - col); bs++)
380                fputc('\b', out);
381            }
382          }
383          break;
384
385        case '\n':
386        case '\r':
387        {
388          /*
389           * Process the command.
390           */
391          if (output)
392            fprintf(out,"\n");
393
394          /*
395           * Only process the command if we have a command and it is not
396           * repeated in the history.
397           */
398          if (strlen(line) == 0) {
399            cmd = -1;
400          } else {
401            if ((cmd < 0) || (strcmp(line, cmds[cmd]) != 0)) {
402              if (count > 1)
403                memmove(cmds[1], cmds[0], (count - 1) * size);
404              memmove (cmds[0], line, size);
405              cmd = 0;
406            } else {
407              if ((cmd > 1) && (strcmp(line, cmds[cmd]) == 0)) {
408                memmove(cmds[1], cmds[0], cmd * size);
409                memmove (cmds[0], line, size);
410                cmd = 0;
411              }
412            }
413          }
414        }
415        return cmd;
416
417        case 16:                         /* Control-P */
418          if ((cmd >= (count - 1)) || (strlen(cmds[cmd + 1]) == 0)) {
419            if (output)
420              fputc('\x7', out);
421            break;
422          }
423
424          up = 1;
425          /* drop through */
426
427        case 14:                        /* Control-N */
428        {
429          int last_cmd = cmd;
430          int clen = strlen (line);
431
432          if (prompt)
433            clen += strlen(prompt);
434
435          if (up) {
436            cmd++;
437          } else {
438            if (cmd < 0) {
439              if (output)
440                fprintf(out, "\x7");
441              break;
442            }
443            else
444              cmd--;
445          }
446
447          if ((last_cmd < 0) || (strcmp(cmds[last_cmd], line) != 0))
448            memcpy (new_line, line, size);
449
450          if (cmd < 0)
451            memcpy (line, new_line, size);
452          else
453            memcpy (line, cmds[cmd], size);
454
455          col = strlen (line);
456
457          if (output) {
458            fprintf(out,"\r%s%*c", prompt, clen, ' ');
459            fprintf(out,"\r%s%s", prompt, line);
460          }
461        }
462        break;
463
464        case 20:                        /* Control-T */
465          if (col > 0)
466          {
467            char tmp;
468            if (col == strlen(line)) {
469              col--;
470              if (output)
471                fprintf(out,"\b");
472            }
473            tmp           = line[col];
474            line[col]     = line[col - 1];
475            line[col - 1] = tmp;
476            if (output)
477              fprintf(out,"\b%c%c", line[col - 1], line[col]);
478            col++;
479          } else {
480            if (output)
481              fputc('\x7', out);
482          }
483          break;
484
485        case 21:                        /* Control-U */
486          if (col > 0)
487          {
488            int clen = strlen (line);
489
490            strcpy (line, line + col);
491            if (output) {
492              fprintf(out,"\r%s%*c", prompt, clen, ' ');
493              fprintf(out,"\r%s%s", prompt, line);
494            }
495            col = 0;
496          }
497          break;
498
499        default:
500          if ((col < (size - 1)) && (c >= ' ') && (c <= '~')) {
501            int end = strlen (line);
502            if (inserting && (col < end) && (end < size)) {
503              int ch, bs;
504              for (ch = end + 1; ch > col; ch--)
505                line[ch] = line[ch - 1];
506              if (output) {
507                fprintf(out, "%s", line + col);
508                for (bs = 0; bs < (end - col + 1); bs++)
509                  fputc('\b', out);
510              }
511            }
512            line[col++] = c;
513            if (col > end)
514              line[col] = '\0';
515            if (output)
516              fputc(c, out);
517          }
518          break;
519      }
520    }
521  }
522  return -2;
523}
524
525/* ----------------------------------------------- *
526 * - The shell TASK
527 * Poor but enough..
528 * TODO: Redirection. Tty Signals. ENVVARs. Shell language.
529 * ----------------------------------------------- */
530
531static bool rtems_shell_login(FILE * in,FILE * out) {
532  rtems_shell_env_t *env;
533  FILE              *fd;
534  int               c;
535  time_t            t;
536
537  env = rtems_shell_get_current_env();
538  assert(env != NULL);
539
540  setuid(0);
541  setgid(0);
542  rtems_current_user_env->euid =
543  rtems_current_user_env->egid =0;
544
545  if (out) {
546    if ((env->devname[5]!='p')||
547        (env->devname[6]!='t')||
548        (env->devname[7]!='y')) {
549      fd = fopen("/etc/issue","r");
550      if (fd) {
551        while ((c=fgetc(fd))!=EOF) {
552          if (c=='@')  {
553            switch(c=fgetc(fd)) {
554              case 'L':
555                fprintf(out,"%s", env->devname);
556                break;
557              case 'B':
558                fprintf(out,"0");
559                break;
560              case 'T':
561              case 'D':
562                time(&t);
563                fprintf(out,"%s",ctime(&t));
564                break;
565              case 'S':
566                fprintf(out,"RTEMS");
567                break;
568              case 'V':
569                fprintf(out,"%s\n%s",_RTEMS_version, _Copyright_Notice);
570                break;
571              case '@':
572                fprintf(out,"@");
573                break;
574              default :
575                fprintf(out,"@%c",c);
576                break;
577            }
578          } else if (c=='\\')  {
579            switch(c=fgetc(fd)) {
580              case '\\': fprintf(out,"\\"); break;
581              case 'b':  fprintf(out,"\b"); break;
582              case 'f':  fprintf(out,"\f"); break;
583              case 'n':  fprintf(out,"\n"); break;
584              case 'r':  fprintf(out,"\r"); break;
585              case 's':  fprintf(out," ");  break;
586              case 't':  fprintf(out,"\t"); break;
587              case '@':  fprintf(out,"@");  break;
588            }
589          } else {
590            fputc(c,out);
591          }
592        }
593        fclose(fd);
594      }
595    } else {
596      fd = fopen("/etc/issue.net","r");
597      if (fd) {
598        while ((c=fgetc(fd))!=EOF) {
599          if (c=='%')  {
600            switch(c=fgetc(fd)) {
601              case 't':
602                fprintf(out,"%s", env->devname);
603                break;
604              case 'h':
605                fprintf(out,"0");
606                break;
607              case 'D':
608                fprintf(out," ");
609                break;
610              case 'd':
611                time(&t);
612                fprintf(out,"%s",ctime(&t));
613                break;
614              case 's':
615                fprintf(out,"RTEMS");
616                break;
617              case 'm':
618                fprintf(out,"(" CPU_NAME "/" CPU_MODEL_NAME ")");
619                break;
620              case 'r':
621                fprintf(out,_RTEMS_version);
622                break;
623              case 'v':
624                fprintf(out,"%s\n%s",_RTEMS_version,_Copyright_Notice);
625                break;
626              case '%':fprintf(out,"%%");
627                break;
628              default:
629                fprintf(out,"%%%c",c);
630                break;
631            }
632          } else {
633            fputc(c,out);
634          }
635        }
636        fclose(fd);
637      }
638    }
639  }
640
641  return rtems_shell_login_prompt(in, out, env->devname, env->login_check);
642}
643
644#if defined(SHELL_DEBUG)
645void rtems_shell_print_env(
646  rtems_shell_env_t * shell_env
647)
648{
649  if ( !shell_env ) {
650    printk( "shell_env is NULL\n" );
651    return;
652  }
653  printk( "shell_env=%p\n"
654    "shell_env->magic=0x%08x\t"
655    "shell_env->devname=%s\n"
656    "shell_env->taskname=%s\t"
657    "shell_env->exit_shell=%d\t"
658    "shell_env->forever=%d\n",
659    shell_env->magic,
660    shell_env->devname,
661    ((shell_env->taskname) ? shell_env->taskname : "NOT SET"),
662    shell_env->exit_shell,
663    shell_env->forever
664  );
665}
666#endif
667
668static rtems_task rtems_shell_task(rtems_task_argument task_argument)
669{
670  rtems_shell_env_t *shell_env = (rtems_shell_env_t*) task_argument;
671  rtems_id           wake_on_end = shell_env->wake_on_end;
672  rtems_shell_main_loop( shell_env );
673  if (wake_on_end != RTEMS_INVALID_ID)
674    rtems_event_send (wake_on_end, RTEMS_EVENT_1);
675  rtems_task_delete( RTEMS_SELF );
676}
677
678#define RTEMS_SHELL_MAXIMUM_ARGUMENTS (128)
679#define RTEMS_SHELL_CMD_SIZE          (128)
680#define RTEMS_SHELL_CMD_COUNT         (32)
681#define RTEMS_SHELL_PROMPT_SIZE       (128)
682
683bool rtems_shell_main_loop(
684  rtems_shell_env_t *shell_env_arg
685)
686{
687  rtems_shell_env_t *shell_env;
688  rtems_shell_cmd_t *shell_cmd;
689  rtems_status_code  sc;
690  int                eno;
691  struct termios     term;
692  struct termios     previous_term;
693  char              *prompt = NULL;
694  int                cmd;
695  int                cmd_count = 1; /* assume a script and so only 1 command line */
696  char              *cmds[RTEMS_SHELL_CMD_COUNT];
697  char              *cmd_argv;
698  int                argc;
699  char              *argv[RTEMS_SHELL_MAXIMUM_ARGUMENTS];
700  bool               result = true;
701  bool               input_file = false;
702  int                line = 0;
703  FILE              *stdinToClose = NULL;
704  FILE              *stdoutToClose = NULL;
705
706  rtems_shell_initialize_command_set();
707
708  eno = pthread_once(&rtems_shell_once, rtems_shell_init_once);
709  assert(eno == 0);
710
711  shell_env = rtems_shell_init_env(shell_env_arg);
712  if (shell_env == NULL) {
713    rtems_error(0, "rtems_shell_init_env");
714    return false;
715  }
716
717  eno = pthread_setspecific(rtems_shell_current_env_key, shell_env);
718  if (eno != 0) {
719    rtems_error(0, "pthread_setspecific(shell_current_env_key)");
720    return false;
721  }
722
723  setuid(0);
724  setgid(0);
725
726  rtems_current_user_env->euid = rtems_current_user_env->egid = 0;
727
728  fileno(stdout);
729
730  /* fprintf( stderr,
731     "-%s-%s-\n", shell_env->input, shell_env->output );
732  */
733
734  if (shell_env->output && strcmp(shell_env->output, "stdout") != 0) {
735    if (strcmp(shell_env->output, "stderr") == 0) {
736      stdout = stderr;
737    } else if (strcmp(shell_env->output, "/dev/null") == 0) {
738      fclose (stdout);
739    } else {
740      FILE *output = fopen(shell_env_arg->output,
741                           shell_env_arg->output_append ? "a" : "w");
742      if (!output) {
743        fprintf(stderr, "shell: open output %s failed: %s\n",
744                shell_env_arg->output, strerror(errno));
745        return false;
746      }
747      stdout = output;
748      stdoutToClose = output;
749    }
750  }
751
752  if (shell_env->input && strcmp(shell_env_arg->input, "stdin") != 0) {
753    FILE *input = fopen(shell_env_arg->input, "r");
754    if (!input) {
755      fprintf(stderr, "shell: open input %s failed: %s\n",
756              shell_env_arg->input, strerror(errno));
757      return false;
758    }
759    stdin = input;
760    stdinToClose = input;
761    shell_env->forever = false;
762    input_file =true;
763  }
764  else {
765    /* make a raw terminal,Linux Manuals */
766    if (tcgetattr(fileno(stdin), &previous_term) >= 0) {
767      term = previous_term;
768      term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
769      term.c_oflag &= ~OPOST;
770      term.c_oflag |= (OPOST|ONLCR); /* But with cr+nl on output */
771      term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
772      term.c_cflag  |= CLOCAL | CREAD;
773      term.c_cc[VMIN]  = 1;
774      term.c_cc[VTIME] = 0;
775      if (tcsetattr (fileno(stdin), TCSADRAIN, &term) < 0) {
776        fprintf(stderr,
777                "shell:cannot set terminal attributes(%s)\n",shell_env->devname);
778      }
779    }
780    cmd_count = RTEMS_SHELL_CMD_COUNT;
781    prompt = malloc(RTEMS_SHELL_PROMPT_SIZE);
782    if (!prompt)
783        fprintf(stderr,
784                "shell:cannot allocate prompt memory\n");
785  }
786
787  setvbuf(stdin,NULL,_IONBF,0); /* Not buffered*/
788  setvbuf(stdout,NULL,_IONBF,0); /* Not buffered*/
789
790  rtems_shell_initialize_command_set();
791
792  /*
793   * Allocate the command line buffers.
794   */
795  cmd_argv = malloc (RTEMS_SHELL_CMD_SIZE);
796  if (!cmd_argv) {
797    fprintf(stderr, "no memory for command line buffers\n" );
798  }
799
800  cmds[0] = calloc (cmd_count, RTEMS_SHELL_CMD_SIZE);
801  if (!cmds[0]) {
802    fprintf(stderr, "no memory for command line buffers\n" );
803  }
804
805  if (cmd_argv && cmds[0]) {
806
807    memset (cmds[0], 0, cmd_count * RTEMS_SHELL_CMD_SIZE);
808
809    for (cmd = 1; cmd < cmd_count; cmd++) {
810      cmds[cmd] = cmds[cmd - 1] + RTEMS_SHELL_CMD_SIZE;
811    }
812
813    do {
814      /* Set again root user and root filesystem, side effect of set_priv..*/
815      sc = rtems_libio_set_private_env();
816      if (sc != RTEMS_SUCCESSFUL) {
817        rtems_error(sc,"rtems_libio_set_private_env():");
818        result = false;
819        break;
820      }
821
822      /*
823       *  By using result here, we can fall to the bottom of the
824       *  loop when the connection is dropped during login and
825       *  keep on trucking.
826       */
827      if (shell_env->login_check != NULL) {
828        result = rtems_shell_login(stdin,stdout);
829      } else {
830        result = true;
831      }
832
833      if (result)  {
834        const char *c;
835        memset (cmds[0], 0, cmd_count * RTEMS_SHELL_CMD_SIZE);
836        if (!input_file) {
837          rtems_shell_cat_file(stdout,"/etc/motd");
838          fprintf(stdout, "\n"
839                  "RTEMS SHELL (Ver.1.0-FRC):%s. " \
840                  __DATE__". 'help' to list commands.\n",
841                  shell_env->devname);
842        }
843
844        if (input_file)
845          chdir(shell_env->cwd);
846        else
847          chdir("/"); /* XXX: chdir to getpwent homedir */
848
849        shell_env->exit_shell = false;
850
851        for (;;) {
852          int cmd;
853
854          /* Prompt section */
855          if (prompt) {
856            rtems_shell_get_prompt(shell_env, prompt,
857                                   RTEMS_SHELL_PROMPT_SIZE);
858          }
859
860          /* getcmd section */
861          cmd = rtems_shell_line_editor(cmds, cmd_count,
862                                        RTEMS_SHELL_CMD_SIZE, prompt,
863                                        stdin, stdout);
864
865          if (cmd == -1)
866            continue; /* empty line */
867
868          if (cmd == -2)
869            break; /*EOF*/
870
871          line++;
872
873          if (shell_env->echo)
874            fprintf(stdout, "%d: %s\n", line, cmds[cmd]);
875
876          /* evaluate cmd section */
877          c = cmds[cmd];
878          while (*c) {
879            if (!isblank((unsigned char)*c))
880              break;
881            c++;
882          }
883
884          if (*c == '\0')   /* empty line */
885            continue;
886
887          if (*c == '#') {  /* comment character */
888            cmds[cmd][0] = 0;
889            continue;
890          }
891
892          if (!strcmp(cmds[cmd],"bye") || !strcmp(cmds[cmd],"exit")) {
893            fprintf(stdout, "Shell exiting\n" );
894            break;
895          } else if (!strcmp(cmds[cmd],"shutdown")) { /* exit application */
896            fprintf(stdout, "System shutting down at user request\n" );
897            exit(0);
898          }
899
900          /* exec cmd section */
901          /* TODO:
902           *  To avoid user crash catch the signals.
903           *  Open a new stdio files with posibility of redirection *
904           *  Run in a new shell task background. (unix &)
905           *  Resuming. A little bash.
906           */
907          memcpy (cmd_argv, cmds[cmd], RTEMS_SHELL_CMD_SIZE);
908          if (!rtems_shell_make_args(cmd_argv, &argc, argv,
909                                     RTEMS_SHELL_MAXIMUM_ARGUMENTS)) {
910            shell_cmd = rtems_shell_lookup_cmd(argv[0]);
911            if ( argv[0] == NULL ) {
912              shell_env->errorlevel = -1;
913            } else if ( shell_cmd == NULL ) {
914              shell_env->errorlevel = rtems_shell_script_file(argc, argv);
915            } else {
916              shell_env->errorlevel = shell_cmd->command(argc, argv);
917            }
918          }
919
920          /* end exec cmd section */
921          if (shell_env->exit_shell)
922            break;
923        }
924
925        fflush( stdout );
926        fflush( stderr );
927      }
928    } while (result && shell_env->forever);
929
930  }
931
932  if (cmds[0])
933    free (cmds[0]);
934  if (cmd_argv)
935    free (cmd_argv);
936  if (prompt)
937    free (prompt);
938
939  if (stdinToClose) {
940    fclose( stdinToClose );
941  } else {
942    if (tcsetattr(fileno(stdin), TCSADRAIN, &previous_term) < 0) {
943      fprintf(
944        stderr,
945        "shell: cannot reset terminal attributes (%s)\n",
946        shell_env->devname
947      );
948    }
949  }
950  if ( stdoutToClose )
951    fclose( stdoutToClose );
952  return result;
953}
954
955/* ----------------------------------------------- */
956static rtems_status_code rtems_shell_run (
957  const char *task_name,
958  size_t task_stacksize,
959  rtems_task_priority task_priority,
960  const char *devname,
961  bool forever,
962  bool wait,
963  const char *input,
964  const char *output,
965  bool output_append,
966  rtems_id wake_on_end,
967  bool echo,
968  rtems_shell_login_check_t login_check
969)
970{
971  rtems_id           task_id;
972  rtems_status_code  sc;
973  rtems_shell_env_t *shell_env;
974  rtems_name         name;
975
976  if ( task_name && strlen(task_name) >= 4)
977    name = rtems_build_name(
978      task_name[0], task_name[1], task_name[2], task_name[3]);
979  else
980    name = rtems_build_name( 'S', 'E', 'N', 'V' );
981
982  sc = rtems_task_create(
983    name,
984    task_priority,
985    task_stacksize,
986    RTEMS_PREEMPT | RTEMS_TIMESLICE | RTEMS_NO_ASR,
987    RTEMS_LOCAL | RTEMS_FLOATING_POINT,
988    &task_id
989  );
990  if (sc != RTEMS_SUCCESSFUL) {
991    rtems_error(sc,"creating task %s in shell_init()",task_name);
992    return sc;
993  }
994
995  shell_env = rtems_shell_init_env( NULL );
996  if ( !shell_env )  {
997   rtems_error(RTEMS_NO_MEMORY,
998               "allocating shell_env %s in shell_init()",task_name);
999   return RTEMS_NO_MEMORY;
1000  }
1001  shell_env->devname       = devname;
1002  shell_env->taskname      = task_name;
1003  shell_env->exit_shell    = false;
1004  shell_env->forever       = forever;
1005  shell_env->echo          = echo;
1006  shell_env->input         = strdup (input);
1007  shell_env->output        = strdup (output);
1008  shell_env->output_append = output_append;
1009  shell_env->wake_on_end   = wake_on_end;
1010  shell_env->login_check   = login_check;
1011
1012  getcwd(shell_env->cwd, sizeof(shell_env->cwd));
1013
1014  sc = rtems_task_start(task_id, rtems_shell_task,
1015                          (rtems_task_argument) shell_env);
1016  if (sc != RTEMS_SUCCESSFUL) {
1017    rtems_error(sc,"starting task %s in shell_init()",task_name);
1018    return sc;
1019  }
1020
1021  if (wait) {
1022    rtems_event_set out;
1023    sc = rtems_event_receive (RTEMS_EVENT_1, RTEMS_WAIT, 0, &out);
1024  }
1025
1026  return 0;
1027}
1028
1029rtems_status_code rtems_shell_init(
1030  const char *task_name,
1031  size_t task_stacksize,
1032  rtems_task_priority task_priority,
1033  const char *devname,
1034  bool forever,
1035  bool wait,
1036  rtems_shell_login_check_t login_check
1037)
1038{
1039  rtems_id to_wake = RTEMS_ID_NONE;
1040
1041  if ( wait )
1042    to_wake = rtems_task_self();
1043
1044  return rtems_shell_run(
1045    task_name,               /* task_name */
1046    task_stacksize,          /* task_stacksize */
1047    task_priority,           /* task_priority */
1048    devname,                 /* devname */
1049    forever,                 /* forever */
1050    wait,                    /* wait */
1051    "stdin",                 /* input */
1052    "stdout",                /* output */
1053    false,                   /* output_append */
1054    to_wake,                 /* wake_on_end */
1055    false,                   /* echo */
1056    login_check              /* login check */
1057  );
1058}
1059
1060rtems_status_code   rtems_shell_script (
1061  const char          *task_name,
1062  size_t               task_stacksize,
1063  rtems_task_priority  task_priority,
1064  const char*          input,
1065  const char*          output,
1066  bool                 output_append,
1067  bool                 wait,
1068  bool                 echo
1069)
1070{
1071  rtems_id          current_task = RTEMS_INVALID_ID;
1072  rtems_status_code sc;
1073
1074  if (wait) {
1075    sc = rtems_task_ident (RTEMS_SELF, RTEMS_LOCAL, &current_task);
1076    if (sc != RTEMS_SUCCESSFUL)
1077      return sc;
1078  }
1079
1080  sc = rtems_shell_run(
1081    task_name,       /* task_name */
1082    task_stacksize,  /* task_stacksize */
1083    task_priority,   /* task_priority */
1084    NULL,            /* devname */
1085    0,               /* forever */
1086    wait,            /* wait */
1087    input,           /* input */
1088    output,          /* output */
1089    output_append,   /* output_append */
1090    current_task,    /* wake_on_end */
1091    echo,            /* echo */
1092    NULL             /* login check */
1093  );
1094  if (sc != RTEMS_SUCCESSFUL)
1095    return sc;
1096
1097  return sc;
1098}
Note: See TracBrowser for help on using the repository browser.