source: rtems/cpukit/libmisc/shell/shell.c @ 4d906d6a

4.11
Last change on this file since 4d906d6a was 4d906d6a, checked in by Chris Johns <chrisj@…>, on Feb 18, 2020 at 12:34:46 AM

libmisc/shell: Fix the handling of joel scripts in telnet

  • Fix the passing of std[in/out] to child threads
  • Fix deleting of managed memory in the key destructor
  • Only set the key in the main loop thread
  • Only allocate a shell env outside of the main loop
  • Fix memory leak if the task start fails
  • Remove error level from shell env, it cannot be returned this way. Add exit_code but the API is broken so it cannot be returned.

Closes #3877

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