source: rtems/cpukit/libmisc/shell/shell.c

Last change on this file was fa25b73, checked in by Ryan Long <thisisryanlong@…>, on Feb 18, 2021 at 2:35:13 PM

shell.c: Fix Dereference before null check (CID #1467420)

CID 1467420: Dereference before null check in rtems_shell_line_editor().

Closes #4254

  • Property mode set to 100644
File size: 33.7 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/shell.h>
27#include <rtems/shellconfig.h>
28#include <rtems/console.h>
29#include "internal.h"
30
31#include <termios.h>
32#include <string.h>
33#include <stdlib.h>
34#include <ctype.h>
35#include <sys/stat.h>
36#include <unistd.h>
37#include <errno.h>
38#include <pwd.h>
39#include <pthread.h>
40#include <assert.h>
41
42#define SHELL_STD_DEBUG 0
43
44#if SHELL_STD_DEBUG
45#include <rtems/bspIo.h>
46#define shell_std_debug(...) \
47  do { printk("shell[%08x]: ", rtems_task_self()); printk(__VA_ARGS__); } while (0)
48#else
49#define shell_std_debug(...)
50#endif
51
52const rtems_shell_env_t rtems_global_shell_env = {
53  .magic         = rtems_build_name('S', 'E', 'N', 'V'),
54  .managed       = false,
55  .devname       = CONSOLE_DEVICE_NAME,
56  .taskname      = "SHGL",
57  .exit_shell    = false,
58  .forever       = true,
59  .echo          = false,
60  .cwd           = "/",
61  .input         = NULL,
62  .output        = NULL,
63  .output_append = false,
64  .parent_stdin  = NULL,
65  .parent_stdout = NULL,
66  .parent_stderr = NULL,
67  .wake_on_end   = RTEMS_ID_NONE,
68  .exit_code     = NULL,
69  .login_check   = NULL,
70  .uid           = 0,
71  .gid           = 0
72};
73
74typedef struct rtems_shell_env_key_handle
75{
76  bool managed;
77  rtems_shell_env_t* env;
78} rtems_shell_env_key_handle;
79
80static pthread_once_t rtems_shell_once = PTHREAD_ONCE_INIT;
81
82static pthread_key_t rtems_shell_current_env_key;
83
84/*
85 *  Initialize the shell user/process environment information
86 */
87static rtems_shell_env_t *rtems_shell_init_env(
88  rtems_shell_env_t *shell_env_parent
89)
90{
91  rtems_shell_env_t *shell_env;
92
93  shell_env = malloc(sizeof(rtems_shell_env_t));
94  if ( !shell_env )
95    return NULL;
96
97  if ( shell_env_parent == NULL ) {
98    shell_env_parent = rtems_shell_get_current_env();
99  }
100  if ( shell_env_parent == NULL ) {
101    *shell_env = rtems_global_shell_env;
102  } else {
103    *shell_env = *shell_env_parent;
104  }
105  shell_env->managed = true;
106  shell_env->taskname = NULL;
107
108  return shell_env;
109}
110
111/*
112 *  Completely free a shell_env_t and all associated memory
113 */
114static void rtems_shell_env_free(
115  void *ptr
116)
117{
118  if ( ptr != NULL ) {
119    rtems_shell_env_key_handle *handle = (rtems_shell_env_key_handle *) ptr;
120    rtems_shell_env_t *shell_env = handle->env;
121
122    if ( handle->managed ) {
123      if ( shell_env->input )
124        free((void *)shell_env->input);
125      if ( shell_env->output )
126        free((void *)shell_env->output);
127      free( shell_env );
128    }
129
130    free( handle );
131  }
132}
133
134static void rtems_shell_create_file(const char *name, const char *content)
135{
136  FILE *fp = fopen(name, "wx");
137
138  if (fp != NULL) {
139    fputs(content, fp);
140    fclose(fp);
141  }
142}
143
144static void rtems_shell_init_commands(void)
145{
146  rtems_shell_cmd_t * const *c;
147  rtems_shell_alias_t * const *a;
148
149  for ( c = rtems_shell_Initial_commands ; *; c++ ) {
150    rtems_shell_add_cmd_struct( *c );
151  }
152
153  for ( a = rtems_shell_Initial_aliases ; *; a++ ) {
154    rtems_shell_alias_cmd( (*a)->name, (*a)->alias );
155  }
156}
157
158static void rtems_shell_init_once(void)
159{
160  struct passwd pwd;
161  struct passwd *pwd_res;
162
163  pthread_key_create(&rtems_shell_current_env_key, rtems_shell_env_free);
164
165  /* dummy call to init /etc dir */
166  getpwuid_r(0, &pwd, NULL, 0, &pwd_res);
167
168  rtems_shell_create_file("etc/issue",
169                          "\n"
170                          "Welcome to @V\\n"
171                          "Login into @S\\n");
172
173  rtems_shell_create_file("/etc/issue.net",
174                          "\n"
175                          "Welcome to %v\n"
176                          "running on %m\n");
177
178  rtems_shell_init_commands();
179  rtems_shell_register_monitor_commands();
180}
181
182void rtems_shell_init_environment(void)
183{
184  assert(pthread_once(&rtems_shell_once, rtems_shell_init_once) == 0);
185}
186
187/*
188 * Set the shell env into the current thread's shell key.
189 */
190static bool rtems_shell_set_shell_env(
191  rtems_shell_env_t* shell_env
192)
193{
194  /*
195   * The shell environment can be managed or it can be provided by a
196   * user. We need to create a handle to hold the env pointer.
197   */
198  rtems_shell_env_key_handle *handle;
199  int eno;
200
201  handle = malloc(sizeof(rtems_shell_env_key_handle));
202  if (handle == NULL) {
203    rtems_error(0, "no memory for shell env key handle)");
204    return false;
205  }
206
207  handle->managed = shell_env->managed;
208  handle->env = shell_env;
209
210  eno = pthread_setspecific(rtems_shell_current_env_key, handle);
211  if (eno != 0) {
212    rtems_error(0, "pthread_setspecific(shell_current_env_key): set");
213    return false;
214  }
215
216  return true;
217}
218
219/*
220 * Clear the current thread's shell key.
221 */
222static void rtems_shell_clear_shell_env(void)
223{
224  int eno;
225
226  /*
227   * Run the destructor manually.
228   */
229  rtems_shell_env_free(pthread_getspecific(rtems_shell_current_env_key));
230
231  /*
232   * Clear the key
233   */
234  eno = pthread_setspecific(rtems_shell_current_env_key, NULL);
235  if (eno != 0)
236    rtems_error(0, "pthread_setspecific(shell_current_env_key): clear");
237}
238
239/*
240 * Clear stdin, stdout and stderr file pointers so they will not be closed.
241 */
242static void rtems_shell_clear_shell_std_handles(void)
243{
244  stdin = NULL;
245  stdout = NULL;
246  stderr = NULL;
247}
248
249/*
250 *  Return the current shell environment
251 */
252rtems_shell_env_t *rtems_shell_get_current_env(void)
253{
254  rtems_shell_env_key_handle *handle;
255  handle = (rtems_shell_env_key_handle*)
256    pthread_getspecific(rtems_shell_current_env_key);
257  return handle == NULL ? NULL : handle->env;
258}
259
260/*
261 *  Duplication the current shell environment and if none is set
262 *  clear it.
263 */
264void rtems_shell_dup_current_env(rtems_shell_env_t *copy)
265{
266  rtems_shell_env_t *env = rtems_shell_get_current_env();
267  if (env != NULL) {
268    shell_std_debug("dup: existing parent\n");
269    *copy = *env;
270  }
271  else {
272    *copy = rtems_global_shell_env;
273    copy->magic         = rtems_build_name('S', 'E', 'N', 'V');
274    copy->devname       = CONSOLE_DEVICE_NAME;
275    copy->taskname      = "RTSH";
276    copy->parent_stdout = stdout;
277    copy->parent_stdin  = stdin;
278    copy->parent_stderr = stderr;
279    shell_std_debug("dup: global: copy: %p out: %d (%p) in: %d (%p)\n",
280                    copy,
281                    fileno(copy->parent_stdout), copy->parent_stdout,
282                    fileno(copy->parent_stdin), copy->parent_stdin);
283  }
284  /*
285   * Duplicated environments are not managed.
286   */
287  copy->managed = false;
288}
289
290/*
291 *  Move a string in a buffer to the left (e.g. when a character
292 *  is deleted). The string must be NUL-terminated and the
293 *  NUL-character will be moved too.
294 */
295static void rtems_shell_move_left(char *start, size_t offset)
296{
297  memmove(start, start + offset, strlen(start + offset) + 1);
298}
299
300/*
301 *  Get a line of user input with modest features
302 */
303static int rtems_shell_line_editor(
304  char       *cmds[],
305  int         count,
306  int         size,
307  const char *prompt,
308  FILE       *in,
309  FILE       *out
310)
311{
312  unsigned int extended_key;
313  int          c;
314  int          col;
315  int          last_col;
316  int          output;
317  char         line[size];
318  char         new_line[size];
319  int          up;
320  int          cmd = -1;
321  int          inserting = 1;
322  int          in_fileno = fileno(in);
323  int          out_fileno;
324
325  _Assert(out != NULL);
326  out_fileno = fileno(out);
327
328  /*
329   * Only this task can use this file descriptor because calling
330   * fileno will block if another thread call made a call on this
331   * descriptor.
332   */
333  output = (out && isatty(in_fileno));
334
335  col = last_col = 0;
336
337  tcdrain(in_fileno);
338  if (out)
339    tcdrain(out_fileno);
340
341  if (output && prompt)
342    fprintf(out, "\r%s", prompt);
343
344  line[0] = 0;
345  new_line[0] = 0;
346
347  for (;;) {
348
349    if (output)
350      fflush(out);
351
352    extended_key = rtems_shell_getchar(in);
353
354    if (extended_key == EOF)
355      return -2;
356
357    c = extended_key & RTEMS_SHELL_KEYS_NORMAL_MASK;
358
359    /*
360     * Make the extended_key usable as a boolean.
361     */
362    extended_key &= ~RTEMS_SHELL_KEYS_NORMAL_MASK;
363
364    up = 0;
365
366    if (extended_key)
367    {
368      switch (c)
369      {
370        case RTEMS_SHELL_KEYS_END:
371          if (output)
372            fprintf(out, "%s", line + col);
373          col = (int) strlen (line);
374          break;
375
376        case RTEMS_SHELL_KEYS_HOME:
377          if (output) {
378            if (prompt)
379              fprintf(out,"\r%s", prompt);
380          }
381          col = 0;
382          break;
383
384        case RTEMS_SHELL_KEYS_LARROW:
385          c = 2;
386          extended_key = 0;
387          break;
388
389        case RTEMS_SHELL_KEYS_RARROW:
390          c = 6;
391          extended_key = 0;
392          break;
393
394        case RTEMS_SHELL_KEYS_UARROW:
395          c = 16;
396          extended_key = 0;
397          break;
398
399        case RTEMS_SHELL_KEYS_DARROW:
400          c = 14;
401          extended_key = 0;
402          break;
403
404        case RTEMS_SHELL_KEYS_DEL:
405          if (line[col] != '\0')
406          {
407            int end;
408            int bs;
409            rtems_shell_move_left(line + col, 1);
410            if (output) {
411              fprintf(out,"\r%s%s ", prompt, line);
412              end = (int) strlen (line);
413              for (bs = 0; bs < ((end - col) + 1); bs++)
414                fputc('\b', out);
415            }
416          }
417          break;
418
419        case RTEMS_SHELL_KEYS_INS:
420          inserting = inserting ? 0 : 1;
421          break;
422      }
423    }
424    if (!extended_key)
425    {
426      switch (c)
427      {
428        case 1:                         /*Control-a*/
429          if (output) {
430            if (prompt)
431              fprintf(out,"\r%s", prompt);
432          }
433          col = 0;
434          break;
435
436        case 2:                         /* Control-B */
437          if (col > 0)
438          {
439            col--;
440            if (output)
441              fputc('\b', out);
442          }
443          break;
444
445        case 4:                         /* Control-D */
446          if (strlen(line)) {
447            if (col < strlen(line)) {
448              rtems_shell_move_left(line + col, 1);
449              if (output) {
450                int bs;
451                fprintf(out,"%s \b", line + col);
452                for (bs = 0; bs < ((int) strlen (line) - col); bs++)
453                  fputc('\b', out);
454              }
455            }
456            break;
457          }
458          /* Fall through */
459
460        case EOF:
461          if (output)
462            fputc('\n', out);
463          return -2;
464
465        case 5:                         /*Control-e*/
466          if (output)
467            fprintf(out, "%s", line + col);
468          col = (int) strlen (line);
469          break;
470
471        case 6:                         /* Control-F */
472          if ((col < size) && (line[col] != '\0')) {
473            if (output)
474              fputc(line[col], out);
475            col++;
476          }
477          break;
478
479        case 7:                         /* Control-G */
480          if (output) {
481            /*
482             * The (int) cast is needed because the width specifier (%*)
483             * must be an int, but strlen() returns a size_t. Without
484             * the case, the result is a printf() format warning.
485             */
486            fprintf(out,"\r%s%*c", prompt, (int) strlen (line), ' ');
487            fprintf(out,"\r%s\x7", prompt);
488          }
489          memset (line, '\0', strlen(line));
490          col = 0;
491          break;
492
493        case 11:                        /*Control-k*/
494          if (line[col]) {
495            if (output) {
496              int end = strlen(line);
497              int bs;
498              fprintf(out,"%*c", end - col, ' ');
499              for (bs = 0; bs < (end - col); bs++)
500                fputc('\b', out);
501            }
502            line[col] = '\0';
503          }
504          break;
505
506        case '\f':
507          if (output) {
508            int end;
509            int bs;
510            fputc('\f',out);
511            fprintf(out,"\r%s%s", prompt, line);
512            end = (int) strlen (line);
513            for (bs = 0; bs < (end - col); bs++)
514              fputc('\b', out);
515          }
516          break;
517
518        case '\b':
519        case '\x7f':
520          if (col > 0)
521          {
522            int bs;
523            col--;
524            rtems_shell_move_left(line + col, 1);
525            if (output) {
526              fprintf(out,"\b%s \b", line + col);
527              for (bs = 0; bs < ((int) strlen (line) - col); bs++)
528                fputc('\b', out);
529            }
530          }
531          break;
532
533        case '\n':
534        case '\r':
535        {
536          /*
537           * Process the command.
538           */
539          if (output)
540            fprintf(out,"\n");
541
542          /*
543           * Only process the command if we have a command and it is not
544           * repeated in the history.
545           */
546          if (strlen(line) == 0) {
547            cmd = -1;
548          } else {
549            if ((cmd < 0) || (strcmp(line, cmds[cmd]) != 0)) {
550              if (count > 1)
551                memmove(cmds[1], cmds[0], (count - 1) * size);
552              memmove (cmds[0], line, size);
553              cmd = 0;
554            } else {
555              if ((cmd > 1) && (strcmp(line, cmds[cmd]) == 0)) {
556                memmove(cmds[1], cmds[0], cmd * size);
557                memmove (cmds[0], line, size);
558                cmd = 0;
559              }
560            }
561          }
562        }
563        return cmd;
564
565        case 16:                         /* Control-P */
566          if ((cmd >= (count - 1)) || (strlen(cmds[cmd + 1]) == 0)) {
567            if (output)
568              fputc('\x7', out);
569            break;
570          }
571
572          up = 1;
573          /* drop through */
574
575        case 14:                        /* Control-N */
576        {
577          int last_cmd = cmd;
578          int clen = strlen (line);
579
580          if (prompt)
581            clen += strlen(prompt);
582
583          if (up) {
584            cmd++;
585          } else {
586            if (cmd < 0) {
587              if (output)
588                fprintf(out, "\x7");
589              break;
590            }
591            else
592              cmd--;
593          }
594
595          if ((last_cmd < 0) || (strcmp(cmds[last_cmd], line) != 0))
596            memcpy (new_line, line, size);
597
598          if (cmd < 0)
599            memcpy (line, new_line, size);
600          else
601            memcpy (line, cmds[cmd], size);
602
603          col = strlen (line);
604
605          if (output) {
606            fprintf(out,"\r%s%*c", prompt, clen, ' ');
607            fprintf(out,"\r%s%s", prompt, line);
608          }
609        }
610        break;
611
612        case 20:                        /* Control-T */
613          if (col > 0)
614          {
615            char tmp;
616            if (col == strlen(line)) {
617              col--;
618              if (output)
619                fprintf(out,"\b");
620            }
621            tmp           = line[col];
622            line[col]     = line[col - 1];
623            line[col - 1] = tmp;
624            if (output)
625              fprintf(out,"\b%c%c", line[col - 1], line[col]);
626            col++;
627          } else {
628            if (output)
629              fputc('\x7', out);
630          }
631          break;
632
633        case 21:                        /* Control-U */
634          if (col > 0)
635          {
636            /* strlen() returns size_t but fprintf("%*...") below requires
637             * int! */
638            int clen = (int) strlen (line);
639            int bs;
640
641            rtems_shell_move_left(line, col);
642            if (output) {
643              fprintf(out,"\r%s%*c", prompt, clen, ' ');
644              fprintf(out,"\r%s%s", prompt, line);
645
646              for (bs = 0; bs < strlen(line); bs++) {
647                fputc('\b', out);
648              }
649            }
650            col = 0;
651          }
652          break;
653
654        default:
655          if ((col < (size - 1)) && (c >= ' ') && (c <= '~')) {
656            int end = strlen (line);
657            if (inserting && (col < end) && (end < size)) {
658              int ch, bs;
659              for (ch = end + 1; ch > col; ch--)
660                line[ch] = line[ch - 1];
661              if (output) {
662                fprintf(out, "%s", line + col);
663                for (bs = 0; bs < (end - col + 1); bs++)
664                  fputc('\b', out);
665              }
666            }
667            line[col++] = c;
668            if (col > end)
669              line[col] = '\0';
670            if (output)
671              fputc(c, out);
672          }
673          break;
674      }
675    }
676  }
677  return -2;
678}
679
680static bool rtems_shell_login(rtems_shell_env_t *env, FILE * in,FILE * out)
681{
682  FILE  *fd;
683  int    c;
684  time_t t;
685
686  if (out) {
687    if ((env->devname[5]!='p')||
688        (env->devname[6]!='t')||
689        (env->devname[7]!='y')) {
690      fd = fopen("/etc/issue","r");
691      if (fd) {
692        while ((c = fgetc(fd)) != EOF) {
693          if (c=='@')  {
694            switch (c = fgetc(fd)) {
695              case 'L':
696                fprintf(out,"%s", env->devname);
697                break;
698              case 'B':
699                fprintf(out,"0");
700                break;
701              case 'T':
702              case 'D':
703                time(&t);
704                fprintf(out,"%s",ctime(&t));
705                break;
706              case 'S':
707                fprintf(out,"RTEMS");
708                break;
709              case 'V':
710                fprintf(
711                  out,
712                  "%s\n%s",
713                  rtems_get_version_string(),
714                  rtems_get_copyright_notice()
715                );
716                break;
717              case '@':
718                fprintf(out,"@");
719                break;
720              default :
721                fprintf(out,"@%c",c);
722                break;
723            }
724          } else if (c=='\\')  {
725            switch(c=fgetc(fd)) {
726              case '\\': fprintf(out,"\\"); break;
727              case 'b':  fprintf(out,"\b"); break;
728              case 'f':  fprintf(out,"\f"); break;
729              case 'n':  fprintf(out,"\n"); break;
730              case 'r':  fprintf(out,"\r"); break;
731              case 's':  fprintf(out," ");  break;
732              case 't':  fprintf(out,"\t"); break;
733              case '@':  fprintf(out,"@");  break;
734            }
735          } else {
736            fputc(c,out);
737          }
738        }
739        fclose(fd);
740      }
741    } else {
742      fd = fopen("/etc/issue.net","r");
743      if (fd) {
744        while ((c=fgetc(fd))!=EOF) {
745          if (c=='%')  {
746            switch(c=fgetc(fd)) {
747              case 't':
748                fprintf(out,"%s", env->devname);
749                break;
750              case 'h':
751                fprintf(out,"0");
752                break;
753              case 'D':
754                fprintf(out," ");
755                break;
756              case 'd':
757                time(&t);
758                fprintf(out,"%s",ctime(&t));
759                break;
760              case 's':
761                fprintf(out,"RTEMS");
762                break;
763              case 'm':
764                fprintf(out,"(" CPU_NAME "/" CPU_MODEL_NAME ")");
765                break;
766              case 'r':
767                fprintf(out,rtems_get_version_string());
768                break;
769              case 'v':
770                fprintf(
771                  out,
772                  "%s\n%s",
773                  rtems_get_version_string(),
774                  rtems_get_copyright_notice()
775                );
776                break;
777              case '%':fprintf(out,"%%");
778                break;
779              default:
780                fprintf(out,"%%%c",c);
781                break;
782            }
783          } else {
784            fputc(c,out);
785          }
786        }
787        fclose(fd);
788      }
789    }
790  }
791
792  return rtems_shell_login_prompt(in, out, env->devname, env->login_check);
793}
794
795#if defined(SHELL_DEBUG)
796void rtems_shell_print_env(
797  rtems_shell_env_t * shell_env
798)
799{
800  if ( !shell_env ) {
801    printk( "shell_env is NULL\n" );
802
803    return;
804  }
805  printk( "shell_env=%p\n"
806    "shell_env->magic=0x%08x\t"
807    "shell_env->devname=%s\n"
808    "shell_env->taskname=%s\t"
809    "shell_env->exit_shell=%d\t"
810    "shell_env->forever=%d\n",
811    shell_env->magic,
812    shell_env->devname,
813    ((shell_env->taskname) ? shell_env->taskname : "NOT SET"),
814    shell_env->exit_shell,
815    shell_env->forever
816  );
817}
818#endif
819
820static rtems_task rtems_shell_task(rtems_task_argument task_argument)
821{
822  rtems_shell_env_t *shell_env = (rtems_shell_env_t*) task_argument;
823  rtems_id           wake_on_end = shell_env->wake_on_end;
824  rtems_shell_main_loop( shell_env );
825  rtems_shell_clear_shell_std_handles();
826  if (wake_on_end != RTEMS_INVALID_ID)
827    rtems_event_send (wake_on_end, RTEMS_EVENT_1);
828  rtems_task_exit();
829}
830
831static bool rtems_shell_init_user_env(void)
832{
833  rtems_status_code sc;
834
835  /* Make sure we have a private user environment */
836  sc = rtems_libio_set_private_env();
837  if (sc != RTEMS_SUCCESSFUL) {
838    rtems_error(sc, "rtems_libio_set_private_env():");
839    return false;
840  }
841
842  /* Make an effective root user */
843  seteuid(0);
844  setegid(0);
845
846  return chroot("/") == 0;
847}
848
849#define RTEMS_SHELL_MAXIMUM_ARGUMENTS (128)
850#define RTEMS_SHELL_CMD_SIZE          (128)
851#define RTEMS_SHELL_CMD_COUNT         (32)
852#define RTEMS_SHELL_PROMPT_SIZE       (128)
853
854bool rtems_shell_main_loop(
855  rtems_shell_env_t *shell_env
856)
857{
858  struct termios  term;
859  struct termios  previous_term;
860  char           *prompt = NULL;
861  int             cmd;
862  int             cmd_count = 1; /* assume a script and so only 1 command line */
863  char           *cmds[RTEMS_SHELL_CMD_COUNT];
864  char           *cmd_argv;
865  int             argc;
866  char           *argv[RTEMS_SHELL_MAXIMUM_ARGUMENTS];
867  bool            result = true;
868  bool            input_file = false;
869  int             line = 0;
870  FILE           *stdinToClose = NULL;
871  FILE           *stdoutToClose = NULL;
872
873  rtems_shell_init_environment();
874
875  if (shell_env->magic != rtems_build_name('S', 'E', 'N', 'V')) {
876    rtems_error(0, "invalid shell environment passed to the main loop)");
877    return false;
878  }
879
880  if (!rtems_shell_set_shell_env(shell_env))
881    return false;
882
883  if (!rtems_shell_init_user_env()) {
884    rtems_error(0, "rtems_shell_init_user_env");
885    rtems_shell_clear_shell_env();
886    return false;
887  }
888
889  shell_std_debug("env: %p\n", shell_env);
890
891  if (shell_env->output == NULL || strcmp(shell_env->output, "stdout") == 0) {
892    if (shell_env->parent_stdout != NULL)
893      stdout = shell_env->parent_stdout;
894  }
895  else if (strcmp(shell_env->output, "stderr") == 0) {
896    if (shell_env->parent_stderr != NULL)
897      stdout = shell_env->parent_stderr;
898    else
899      stdout = stderr;
900  } else if (strcmp(shell_env->output, "/dev/null") == 0) {
901    if (stdout == NULL) {
902      fprintf(stderr, "shell: stdout is NULLs\n");
903      rtems_shell_clear_shell_env();
904      return false;
905    }
906    fclose (stdout);
907  } else {
908    FILE *output = fopen(shell_env->output,
909                         shell_env->output_append ? "a" : "w");
910    if (output == NULL) {
911      fprintf(stderr, "shell: open output %s failed: %s\n",
912              shell_env->output, strerror(errno));
913      rtems_shell_clear_shell_env();
914      return false;
915    }
916    stdout = output;
917    stdoutToClose = output;
918  }
919
920  if (shell_env->input == NULL || strcmp(shell_env->input, "stdin") == 0) {
921    if (shell_env->parent_stdin != NULL)
922      stdin = shell_env->parent_stdin;
923  } else {
924    FILE *input = fopen(shell_env->input, "r");
925    if (input == NULL) {
926      fprintf(stderr, "shell: open input %s failed: %s\n",
927              shell_env->input, strerror(errno));
928      if (stdoutToClose != NULL)
929        fclose(stdoutToClose);
930      rtems_shell_clear_shell_env();
931      return false;
932    }
933    stdin = input;
934    stdinToClose = input;
935    shell_env->forever = false;
936    input_file = true;
937  }
938
939  if (!input_file) {
940    if (stdin == NULL) {
941      fprintf(stderr, "shell: stdin is NULLs\n");
942      if (stdoutToClose != NULL)
943        fclose(stdoutToClose);
944      rtems_shell_clear_shell_env();
945      return false;
946    }
947    /* Make a raw terminal, Linux Manuals */
948    if (tcgetattr(fileno(stdin), &previous_term) >= 0) {
949      term = previous_term;
950      term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
951      term.c_oflag &= ~OPOST;
952      term.c_oflag |= (OPOST|ONLCR); /* But with cr+nl on output */
953      term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
954      term.c_cflag |= CLOCAL | CREAD;
955      term.c_cc[VMIN]  = 1;
956      term.c_cc[VTIME] = 0;
957      if (tcsetattr (fileno(stdin), TCSADRAIN, &term) < 0) {
958        fprintf(stderr,
959                "shell: cannot set terminal attributes(%s)\n",shell_env->devname);
960      }
961    }
962    cmd_count = RTEMS_SHELL_CMD_COUNT;
963    prompt = malloc(RTEMS_SHELL_PROMPT_SIZE);
964    if (!prompt)
965        fprintf(stderr,
966                "shell: cannot allocate prompt memory\n");
967  }
968
969  shell_std_debug("child out: %d (%p)\n", fileno(stdout), stdout);
970  shell_std_debug("child  in: %d (%p)\n", fileno(stdin), stdin);
971
972   /* Do not buffer if interactive else leave buffered */
973  if (!input_file)
974    setvbuf(stdin, NULL, _IONBF, 0);
975  setvbuf(stdout, NULL, _IONBF, 0);
976
977  /*
978   * Allocate the command line buffers.
979   */
980  cmd_argv = malloc (RTEMS_SHELL_CMD_SIZE);
981  if (!cmd_argv) {
982    fprintf(stderr, "no memory for command line buffers\n" );
983  }
984
985  cmds[0] = calloc (cmd_count, RTEMS_SHELL_CMD_SIZE);
986  if (!cmds[0]) {
987    fprintf(stderr, "no memory for command line buffers\n" );
988  }
989
990  if (cmd_argv && cmds[0]) {
991
992    memset (cmds[0], 0, cmd_count * RTEMS_SHELL_CMD_SIZE);
993
994    for (cmd = 1; cmd < cmd_count; cmd++) {
995      cmds[cmd] = cmds[cmd - 1] + RTEMS_SHELL_CMD_SIZE;
996    }
997
998    do {
999      result = rtems_shell_init_user_env();
1000
1001      if (result) {
1002        /*
1003         *  By using result here, we can fall to the bottom of the
1004         *  loop when the connection is dropped during login and
1005         *  keep on trucking.
1006         */
1007        if (shell_env->login_check != NULL) {
1008          result = rtems_shell_login(shell_env, stdin, stdout);
1009        } else {
1010          setuid(shell_env->uid);
1011          setgid(shell_env->gid);
1012          seteuid(shell_env->uid);
1013          setegid(shell_env->gid);
1014          rtems_current_user_env_getgroups();
1015
1016          result = true;
1017        }
1018      }
1019
1020      if (result)  {
1021        const char *c;
1022        memset (cmds[0], 0, cmd_count * RTEMS_SHELL_CMD_SIZE);
1023        if (!input_file) {
1024          rtems_shell_cat_file(stdout,"/etc/motd");
1025          fprintf(stdout, "\n"
1026                  "RTEMS Shell on %s. Use 'help' to list commands.\n",
1027                  shell_env->devname);
1028        }
1029
1030        if (input_file)
1031          chdir(shell_env->cwd);
1032        else
1033          chdir("/"); /* XXX: chdir to getpwent homedir */
1034
1035        shell_env->exit_shell = false;
1036
1037        for (;;) {
1038          /* Prompt section */
1039          if (prompt) {
1040            rtems_shell_get_prompt(shell_env, prompt,
1041                                   RTEMS_SHELL_PROMPT_SIZE);
1042          }
1043
1044          /* getcmd section */
1045          cmd = rtems_shell_line_editor(cmds, cmd_count,
1046                                        RTEMS_SHELL_CMD_SIZE, prompt,
1047                                        stdin, stdout);
1048
1049          if (cmd == -1)
1050            continue; /* empty line */
1051
1052          if (cmd == -2) {
1053            result = false;
1054            break; /*EOF*/
1055          }
1056
1057          line++;
1058
1059          if (shell_env->echo)
1060            fprintf(stdout, "%d: %s\n", line, cmds[cmd]);
1061
1062          /* evaluate cmd section */
1063          c = cmds[cmd];
1064          while (*c) {
1065            if (!isblank((unsigned char)*c))
1066              break;
1067            c++;
1068          }
1069
1070          if (*c == '\0')   /* empty line */
1071            continue;
1072
1073          if (*c == '#') {  /* comment character */
1074            cmds[cmd][0] = 0;
1075            continue;
1076          }
1077
1078          if (!strcmp(cmds[cmd],"bye") || !strcmp(cmds[cmd],"exit")) {
1079            fprintf(stdout, "Shell exiting\n" );
1080            break;
1081          }
1082
1083          /* exec cmd section */
1084          /* TODO:
1085           *  To avoid user crash catch the signals.
1086           *  Open a new stdio files with posibility of redirection *
1087           *  Run in a new shell task background. (unix &)
1088           *  Resuming. A little bash.
1089           */
1090          memcpy (cmd_argv, cmds[cmd], RTEMS_SHELL_CMD_SIZE);
1091          if (!rtems_shell_make_args(cmd_argv, &argc, argv,
1092                                     RTEMS_SHELL_MAXIMUM_ARGUMENTS)) {
1093            int exit_code = rtems_shell_execute_cmd(argv[0], argc, argv);
1094            if (shell_env->exit_code != NULL)
1095              *shell_env->exit_code = exit_code;
1096            if (exit_code != 0 && shell_env->exit_on_error)
1097              shell_env->exit_shell = true;
1098          }
1099
1100          /* end exec cmd section */
1101          if (shell_env->exit_shell)
1102            break;
1103        }
1104
1105        fflush( stdout );
1106        fflush( stderr );
1107      }
1108      shell_std_debug("end: %d %d\n", result, shell_env->forever);
1109    } while (result && shell_env->forever);
1110
1111  }
1112
1113  if (cmds[0])
1114    free (cmds[0]);
1115  if (cmd_argv)
1116    free (cmd_argv);
1117  if (prompt)
1118    free (prompt);
1119
1120  shell_std_debug("child in-to-close: %p\n", stdinToClose);
1121  shell_std_debug("child out-to-close: %p\n", stdoutToClose);
1122
1123  if (stdinToClose) {
1124    fclose( stdinToClose );
1125  } else {
1126    if (tcsetattr(fileno(stdin), TCSADRAIN, &previous_term) < 0) {
1127      fprintf(
1128        stderr,
1129        "shell: cannot reset terminal attributes (%s)\n",
1130        shell_env->devname
1131      );
1132    }
1133  }
1134  if ( stdoutToClose )
1135    fclose( stdoutToClose );
1136  rtems_shell_clear_shell_env();
1137  return result;
1138}
1139
1140/* ----------------------------------------------- */
1141static rtems_status_code rtems_shell_run (
1142  const char *task_name,
1143  size_t task_stacksize,
1144  rtems_task_priority task_priority,
1145  const char *devname,
1146  bool forever,
1147  bool wait,
1148  const char *input,
1149  const char *output,
1150  bool output_append,
1151  rtems_id wake_on_end,
1152  int *exit_code,
1153  bool echo,
1154  rtems_shell_login_check_t login_check
1155)
1156{
1157  rtems_id           task_id;
1158  rtems_status_code  sc;
1159  rtems_shell_env_t *shell_env;
1160  rtems_name         name;
1161
1162  rtems_shell_init_environment();
1163
1164  if ( task_name && strlen(task_name) >= 4)
1165    name = rtems_build_name(
1166      task_name[0], task_name[1], task_name[2], task_name[3]);
1167  else
1168    name = rtems_build_name( 'S', 'E', 'N', 'V' );
1169
1170  sc = rtems_task_create(
1171    name,
1172    task_priority,
1173    task_stacksize,
1174    RTEMS_PREEMPT | RTEMS_TIMESLICE | RTEMS_NO_ASR,
1175    RTEMS_LOCAL | RTEMS_FLOATING_POINT,
1176    &task_id
1177  );
1178  if (sc != RTEMS_SUCCESSFUL) {
1179    rtems_error(sc,"creating task %s in shell_init()",task_name);
1180    return sc;
1181  }
1182
1183  shell_env = rtems_shell_init_env( NULL );
1184  if ( !shell_env )  {
1185   rtems_error(RTEMS_NO_MEMORY,
1186               "allocating shell_env %s in shell_init()",task_name);
1187   return RTEMS_NO_MEMORY;
1188  }
1189
1190  shell_std_debug("run: env: %p\n", shell_env);
1191
1192  shell_env->devname       = devname;
1193  shell_env->taskname      = task_name;
1194
1195  shell_env->exit_shell    = false;
1196  shell_env->forever       = forever;
1197  shell_env->echo          = echo;
1198  shell_env->input         = input == NULL ? NULL : strdup (input);
1199  shell_env->output        = output == NULL ? NULL : strdup (output);
1200  shell_env->output_append = output_append;
1201  shell_env->parent_stdin  = stdin;
1202  shell_env->parent_stdout = stdout;
1203  shell_env->parent_stderr = stderr;
1204  shell_env->wake_on_end   = wake_on_end;
1205  shell_env->exit_code     = exit_code;
1206  shell_env->login_check   = login_check;
1207  shell_env->uid           = getuid();
1208  shell_env->gid           = getgid();
1209
1210  getcwd(shell_env->cwd, sizeof(shell_env->cwd));
1211
1212  shell_std_debug("run out: %d (%p)\n",
1213                  fileno(shell_env->parent_stdout), shell_env->parent_stdout);
1214  shell_std_debug("run  in: %d (%p)\n",
1215                  fileno(shell_env->parent_stdin), shell_env->parent_stdin);
1216
1217  sc = rtems_task_start(task_id, rtems_shell_task,
1218                        (rtems_task_argument) shell_env);
1219  if (sc != RTEMS_SUCCESSFUL) {
1220    rtems_error(sc,"starting task %s in shell_init()",task_name);
1221    free( (void*) shell_env->input );
1222    free( (void*) shell_env->output );
1223    free( shell_env );
1224    return sc;
1225  }
1226
1227  if (wait) {
1228    rtems_event_set out;
1229    sc = rtems_event_receive (RTEMS_EVENT_1, RTEMS_WAIT, 0, &out);
1230  }
1231
1232  shell_std_debug("run: end: sc:%d ec:%d\n", sc, *exit_code);
1233
1234  return sc;
1235}
1236
1237rtems_status_code rtems_shell_init(
1238  const char *task_name,
1239  size_t task_stacksize,
1240  rtems_task_priority task_priority,
1241  const char *devname,
1242  bool forever,
1243  bool wait,
1244  rtems_shell_login_check_t login_check
1245)
1246{
1247  rtems_id to_wake = RTEMS_ID_NONE;
1248  int exit_code = 0;
1249
1250  if ( wait )
1251    to_wake = rtems_task_self();
1252
1253  return rtems_shell_run(
1254    task_name,               /* task_name */
1255    task_stacksize,          /* task_stacksize */
1256    task_priority,           /* task_priority */
1257    devname,                 /* devname */
1258    forever,                 /* forever */
1259    wait,                    /* wait */
1260    "stdin",                 /* input */
1261    "stdout",                /* output */
1262    false,                   /* output_append */
1263    to_wake,                 /* wake_on_end */
1264    &exit_code,              /* exit code of command */
1265    false,                   /* echo */
1266    login_check              /* login check */
1267  );
1268}
1269
1270rtems_status_code rtems_shell_script (
1271  const char          *task_name,
1272  size_t               task_stacksize,
1273  rtems_task_priority  task_priority,
1274  const char*          input,
1275  const char*          output,
1276  bool                 output_append,
1277  bool                 wait,
1278  bool                 echo
1279)
1280{
1281  rtems_id to_wake = RTEMS_ID_NONE;
1282  int exit_code = 0;
1283  rtems_status_code sc;
1284
1285  shell_std_debug("script: in: %s out: %s\n", input, output);
1286
1287  if ( wait )
1288    to_wake = rtems_task_self();
1289
1290  sc = rtems_shell_run(
1291    task_name,       /* task_name */
1292    task_stacksize,  /* task_stacksize */
1293    task_priority,   /* task_priority */
1294    NULL,            /* devname */
1295    0,               /* forever */
1296    wait,            /* wait */
1297    input,           /* input */
1298    output,          /* output */
1299    output_append,   /* output_append */
1300    to_wake,         /* wake_on_end */
1301    &exit_code,      /* exit_code */
1302    echo,            /* echo */
1303    NULL             /* login check */
1304  );
1305
1306  if (sc == RTEMS_SUCCESSFUL)
1307  {
1308    /* Place holder until RTEMS 5 is released then the interface for
1309     * this call will change. */
1310  }
1311
1312  shell_std_debug("script: end: %d\n", sc);
1313
1314  return sc;
1315}
Note: See TracBrowser for help on using the repository browser.