source: rtems/cpukit/libmisc/shell/shell.c @ 95036a45

5
Last change on this file since 95036a45 was 95036a45, checked in by Chris Johns <chrisj@…>, on Aug 8, 2020 at 8:07:32 AM

shell: Only clear std handles when the shell task exits

Clearing the std file handles when the main loop exited crashes
telnetd as it reuses its session threads.

Closes #3859

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