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

5
Last change on this file since d007cc2 was d007cc2, checked in by Chris Johns <chrisj@…>, on Apr 14, 2020 at 5:26:27 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 #3859

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