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

5
Last change on this file since d30a352 was 1af8e45b, checked in by Sebastian Huber <sebastian.huber@…>, on 05/05/20 at 14:11:39

rtems: Add rtems_get_copyright_notice()

Update #3973.

  • Property mode set to 100644
File size: 32.7 KB
Line 
1/*
2 *
3 *  Instantatiate a new terminal shell.
4 *
5 *  Author:
6 *
7 *   WORK: fernando.ruiz@ctv.es
8 *   HOME: correo@fernando-ruiz.com
9 *
10 *  The license and distribution terms for this file may be
11 *  found in the file LICENSE in this distribution or at
12 *  http://www.rtems.org/license/LICENSE.
13 */
14
15#ifdef HAVE_CONFIG_H
16#include "config.h"
17#endif
18
19#include <stdio.h>
20#include <time.h>
21
22#include <rtems.h>
23#include <rtems/error.h>
24#include <rtems/libio.h>
25#include <rtems/libio_.h>
26#include <rtems/shell.h>
27#include <rtems/shellconfig.h>
28#include <rtems/console.h>
29#include "internal.h"
30
31#include <termios.h>
32#include <string.h>
33#include <stdlib.h>
34#include <ctype.h>
35#include <sys/stat.h>
36#include <unistd.h>
37#include <errno.h>
38#include <pwd.h>
39#include <pthread.h>
40#include <assert.h>
41
42#define SHELL_STD_DEBUG 0
43
44#if SHELL_STD_DEBUG
45#include <rtems/bspIo.h>
46#define shell_std_debug(...) \
47  do { printk("shell[%08x]: ", rtems_task_self()); printk(__VA_ARGS__); } while (0)
48#else
49#define shell_std_debug(...)
50#endif
51
52const rtems_shell_env_t rtems_global_shell_env = {
53  .magic         = rtems_build_name('S', 'E', 'N', 'V'),
54  .managed       = false,
55  .devname       = CONSOLE_DEVICE_NAME,
56  .taskname      = "SHGL",
57  .exit_shell    = false,
58  .forever       = true,
59  .echo          = false,
60  .cwd           = "/",
61  .input         = NULL,
62  .output        = NULL,
63  .output_append = false,
64  .parent_stdin  = NULL,
65  .parent_stdout = NULL,
66  .parent_stderr = NULL,
67  .wake_on_end   = RTEMS_ID_NONE,
68  .exit_code     = NULL,
69  .login_check   = NULL,
70  .uid           = 0,
71  .gid           = 0
72};
73
74typedef struct rtems_shell_env_key_handle
75{
76  bool managed;
77  rtems_shell_env_t* env;
78} rtems_shell_env_key_handle;
79
80static pthread_once_t rtems_shell_once = PTHREAD_ONCE_INIT;
81
82static pthread_key_t rtems_shell_current_env_key;
83
84/*
85 *  Initialize the shell user/process environment information
86 */
87static rtems_shell_env_t *rtems_shell_init_env(
88  rtems_shell_env_t *shell_env_parent
89)
90{
91  rtems_shell_env_t *shell_env;
92
93  shell_env = malloc(sizeof(rtems_shell_env_t));
94  if ( !shell_env )
95    return NULL;
96
97  if ( shell_env_parent == NULL ) {
98    shell_env_parent = rtems_shell_get_current_env();
99  }
100  if ( shell_env_parent == NULL ) {
101    *shell_env = rtems_global_shell_env;
102  } else {
103    *shell_env = *shell_env_parent;
104  }
105  shell_env->managed = true;
106  shell_env->taskname = NULL;
107
108  return shell_env;
109}
110
111/*
112 *  Completely free a shell_env_t and all associated memory
113 */
114static void rtems_shell_env_free(
115  void *ptr
116)
117{
118  if ( ptr != NULL ) {
119    rtems_shell_env_key_handle *handle = (rtems_shell_env_key_handle *) ptr;
120    rtems_shell_env_t *shell_env = handle->env;
121
122    if ( handle->managed ) {
123      if ( shell_env->input )
124        free((void *)shell_env->input);
125      if ( shell_env->output )
126        free((void *)shell_env->output);
127      free( shell_env );
128    }
129
130    free( handle );
131  }
132}
133
134static void rtems_shell_create_file(const char *name, const char *content)
135{
136  FILE *fp = fopen(name, "wx");
137
138  if (fp != NULL) {
139    fputs(content, fp);
140    fclose(fp);
141  }
142}
143
144static void rtems_shell_init_commands(void)
145{
146  rtems_shell_cmd_t * const *c;
147  rtems_shell_alias_t * const *a;
148
149  for ( c = rtems_shell_Initial_commands ; *c  ; c++ ) {
150    rtems_shell_add_cmd_struct( *c );
151  }
152
153  for ( a = rtems_shell_Initial_aliases ; *a  ; 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(
687                  out,
688                  "%s\n%s",
689                  rtems_get_version_string(),
690                  rtems_get_copyright_notice()
691                );
692                break;
693              case '@':
694                fprintf(out,"@");
695                break;
696              default :
697                fprintf(out,"@%c",c);
698                break;
699            }
700          } else if (c=='\\')  {
701            switch(c=fgetc(fd)) {
702              case '\\': fprintf(out,"\\"); break;
703              case 'b':  fprintf(out,"\b"); break;
704              case 'f':  fprintf(out,"\f"); break;
705              case 'n':  fprintf(out,"\n"); break;
706              case 'r':  fprintf(out,"\r"); break;
707              case 's':  fprintf(out," ");  break;
708              case 't':  fprintf(out,"\t"); break;
709              case '@':  fprintf(out,"@");  break;
710            }
711          } else {
712            fputc(c,out);
713          }
714        }
715        fclose(fd);
716      }
717    } else {
718      fd = fopen("/etc/issue.net","r");
719      if (fd) {
720        while ((c=fgetc(fd))!=EOF) {
721          if (c=='%')  {
722            switch(c=fgetc(fd)) {
723              case 't':
724                fprintf(out,"%s", env->devname);
725                break;
726              case 'h':
727                fprintf(out,"0");
728                break;
729              case 'D':
730                fprintf(out," ");
731                break;
732              case 'd':
733                time(&t);
734                fprintf(out,"%s",ctime(&t));
735                break;
736              case 's':
737                fprintf(out,"RTEMS");
738                break;
739              case 'm':
740                fprintf(out,"(" CPU_NAME "/" CPU_MODEL_NAME ")");
741                break;
742              case 'r':
743                fprintf(out,rtems_get_version_string());
744                break;
745              case 'v':
746                fprintf(
747                  out,
748                  "%s\n%s",
749                  rtems_get_version_string(),
750                  rtems_get_copyright_notice()
751                );
752                break;
753              case '%':fprintf(out,"%%");
754                break;
755              default:
756                fprintf(out,"%%%c",c);
757                break;
758            }
759          } else {
760            fputc(c,out);
761          }
762        }
763        fclose(fd);
764      }
765    }
766  }
767
768  return rtems_shell_login_prompt(in, out, env->devname, env->login_check);
769}
770
771#if defined(SHELL_DEBUG)
772void rtems_shell_print_env(
773  rtems_shell_env_t * shell_env
774)
775{
776  if ( !shell_env ) {
777    printk( "shell_env is NULL\n" );
778    return;
779  }
780  printk( "shell_env=%p\n"
781    "shell_env->magic=0x%08x\t"
782    "shell_env->devname=%s\n"
783    "shell_env->taskname=%s\t"
784    "shell_env->exit_shell=%d\t"
785    "shell_env->forever=%d\n",
786    shell_env->magic,
787    shell_env->devname,
788    ((shell_env->taskname) ? shell_env->taskname : "NOT SET"),
789    shell_env->exit_shell,
790    shell_env->forever
791  );
792}
793#endif
794
795static rtems_task rtems_shell_task(rtems_task_argument task_argument)
796{
797  rtems_shell_env_t *shell_env = (rtems_shell_env_t*) task_argument;
798  rtems_id           wake_on_end = shell_env->wake_on_end;
799  rtems_shell_main_loop( shell_env );
800  if (wake_on_end != RTEMS_INVALID_ID)
801    rtems_event_send (wake_on_end, RTEMS_EVENT_1);
802  rtems_task_exit();
803}
804
805static bool rtems_shell_init_user_env(void)
806{
807  rtems_status_code sc;
808
809  /* Make sure we have a private user environment */
810  sc = rtems_libio_set_private_env();
811  if (sc != RTEMS_SUCCESSFUL) {
812    rtems_error(sc, "rtems_libio_set_private_env():");
813    return false;
814  }
815
816  /* Make an effective root user */
817  seteuid(0);
818  setegid(0);
819
820  return chroot("/") == 0;
821}
822
823#define RTEMS_SHELL_MAXIMUM_ARGUMENTS (128)
824#define RTEMS_SHELL_CMD_SIZE          (128)
825#define RTEMS_SHELL_CMD_COUNT         (32)
826#define RTEMS_SHELL_PROMPT_SIZE       (128)
827
828bool rtems_shell_main_loop(
829  rtems_shell_env_t *shell_env
830)
831{
832  struct termios  term;
833  struct termios  previous_term;
834  char           *prompt = NULL;
835  int             cmd;
836  int             cmd_count = 1; /* assume a script and so only 1 command line */
837  char           *cmds[RTEMS_SHELL_CMD_COUNT];
838  char           *cmd_argv;
839  int             argc;
840  char           *argv[RTEMS_SHELL_MAXIMUM_ARGUMENTS];
841  bool            result = true;
842  bool            input_file = false;
843  int             line = 0;
844  FILE           *stdinToClose = NULL;
845  FILE           *stdoutToClose = NULL;
846
847  rtems_shell_init_environment();
848
849  if (shell_env->magic != rtems_build_name('S', 'E', 'N', 'V')) {
850    rtems_error(0, "invalid shell environment passed to the main loop)");
851    return false;
852  }
853
854  if (!rtems_shell_set_shell_env(shell_env))
855    return false;
856
857  if (!rtems_shell_init_user_env()) {
858    rtems_error(0, "rtems_shell_init_user_env");
859    rtems_shell_clear_shell_env();
860    return false;
861  }
862
863  shell_std_debug("env: %p\n", shell_env);
864
865  if (shell_env->output == NULL || strcmp(shell_env->output, "stdout") == 0) {
866    if (shell_env->parent_stdout != NULL)
867      stdout = shell_env->parent_stdout;
868  }
869  else if (strcmp(shell_env->output, "stderr") == 0) {
870    if (shell_env->parent_stderr != NULL)
871      stdout = shell_env->parent_stderr;
872    else
873      stdout = stderr;
874  } else if (strcmp(shell_env->output, "/dev/null") == 0) {
875    fclose (stdout);
876  } else {
877    FILE *output = fopen(shell_env->output,
878                         shell_env->output_append ? "a" : "w");
879    if (output == NULL) {
880      fprintf(stderr, "shell: open output %s failed: %s\n",
881              shell_env->output, strerror(errno));
882      rtems_shell_clear_shell_env();
883      return false;
884    }
885    stdout = output;
886    stdoutToClose = output;
887  }
888
889  if (shell_env->input == NULL || strcmp(shell_env->input, "stdin") == 0) {
890    if (shell_env->parent_stdin != NULL)
891      stdin = shell_env->parent_stdin;
892  } else {
893    FILE *input = fopen(shell_env->input, "r");
894    if (input == NULL) {
895      fprintf(stderr, "shell: open input %s failed: %s\n",
896              shell_env->input, strerror(errno));
897      if (stdoutToClose != NULL)
898        fclose(stdoutToClose);
899      rtems_shell_clear_shell_env();
900      return false;
901    }
902    stdin = input;
903    stdinToClose = input;
904    shell_env->forever = false;
905    input_file = true;
906  }
907
908  if (!input_file) {
909    /* Make a raw terminal, Linux Manuals */
910    if (tcgetattr(fileno(stdin), &previous_term) >= 0) {
911      term = previous_term;
912      term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
913      term.c_oflag &= ~OPOST;
914      term.c_oflag |= (OPOST|ONLCR); /* But with cr+nl on output */
915      term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
916      term.c_cflag |= CLOCAL | CREAD;
917      term.c_cc[VMIN]  = 1;
918      term.c_cc[VTIME] = 0;
919      if (tcsetattr (fileno(stdin), TCSADRAIN, &term) < 0) {
920        fprintf(stderr,
921                "shell: cannot set terminal attributes(%s)\n",shell_env->devname);
922      }
923    }
924    cmd_count = RTEMS_SHELL_CMD_COUNT;
925    prompt = malloc(RTEMS_SHELL_PROMPT_SIZE);
926    if (!prompt)
927        fprintf(stderr,
928                "shell: cannot allocate prompt memory\n");
929  }
930
931  shell_std_debug("child out: %d (%p)\n", fileno(stdout), stdout);
932  shell_std_debug("child  in: %d (%p)\n", fileno(stdin), stdin);
933
934   /* Do not buffer if interactive else leave buffered */
935  if (!input_file)
936    setvbuf(stdin, NULL, _IONBF, 0);
937  setvbuf(stdout, NULL, _IONBF, 0);
938
939  /*
940   * Allocate the command line buffers.
941   */
942  cmd_argv = malloc (RTEMS_SHELL_CMD_SIZE);
943  if (!cmd_argv) {
944    fprintf(stderr, "no memory for command line buffers\n" );
945  }
946
947  cmds[0] = calloc (cmd_count, RTEMS_SHELL_CMD_SIZE);
948  if (!cmds[0]) {
949    fprintf(stderr, "no memory for command line buffers\n" );
950  }
951
952  if (cmd_argv && cmds[0]) {
953
954    memset (cmds[0], 0, cmd_count * RTEMS_SHELL_CMD_SIZE);
955
956    for (cmd = 1; cmd < cmd_count; cmd++) {
957      cmds[cmd] = cmds[cmd - 1] + RTEMS_SHELL_CMD_SIZE;
958    }
959
960    do {
961      result = rtems_shell_init_user_env();
962
963      if (result) {
964        /*
965         *  By using result here, we can fall to the bottom of the
966         *  loop when the connection is dropped during login and
967         *  keep on trucking.
968         */
969        if (shell_env->login_check != NULL) {
970          result = rtems_shell_login(shell_env, stdin,stdout);
971        } else {
972          setuid(shell_env->uid);
973          setgid(shell_env->gid);
974          seteuid(shell_env->uid);
975          setegid(shell_env->gid);
976          rtems_current_user_env_getgroups();
977
978          result = true;
979        }
980      }
981
982      if (result)  {
983        const char *c;
984        memset (cmds[0], 0, cmd_count * RTEMS_SHELL_CMD_SIZE);
985        if (!input_file) {
986          rtems_shell_cat_file(stdout,"/etc/motd");
987          fprintf(stdout, "\n"
988                  "RTEMS Shell on %s. Use 'help' to list commands.\n",
989                  shell_env->devname);
990        }
991
992        if (input_file)
993          chdir(shell_env->cwd);
994        else
995          chdir("/"); /* XXX: chdir to getpwent homedir */
996
997        shell_env->exit_shell = false;
998
999        for (;;) {
1000          /* Prompt section */
1001          if (prompt) {
1002            rtems_shell_get_prompt(shell_env, prompt,
1003                                   RTEMS_SHELL_PROMPT_SIZE);
1004          }
1005
1006          /* getcmd section */
1007          cmd = rtems_shell_line_editor(cmds, cmd_count,
1008                                        RTEMS_SHELL_CMD_SIZE, prompt,
1009                                        stdin, stdout);
1010
1011          if (cmd == -1)
1012            continue; /* empty line */
1013
1014          if (cmd == -2) {
1015            result = false;
1016            break; /*EOF*/
1017          }
1018
1019          line++;
1020
1021          if (shell_env->echo)
1022            fprintf(stdout, "%d: %s\n", line, cmds[cmd]);
1023
1024          /* evaluate cmd section */
1025          c = cmds[cmd];
1026          while (*c) {
1027            if (!isblank((unsigned char)*c))
1028              break;
1029            c++;
1030          }
1031
1032          if (*c == '\0')   /* empty line */
1033            continue;
1034
1035          if (*c == '#') {  /* comment character */
1036            cmds[cmd][0] = 0;
1037            continue;
1038          }
1039
1040          if (!strcmp(cmds[cmd],"bye") || !strcmp(cmds[cmd],"exit")) {
1041            fprintf(stdout, "Shell exiting\n" );
1042            break;
1043          }
1044
1045          /* exec cmd section */
1046          /* TODO:
1047           *  To avoid user crash catch the signals.
1048           *  Open a new stdio files with posibility of redirection *
1049           *  Run in a new shell task background. (unix &)
1050           *  Resuming. A little bash.
1051           */
1052          memcpy (cmd_argv, cmds[cmd], RTEMS_SHELL_CMD_SIZE);
1053          if (!rtems_shell_make_args(cmd_argv, &argc, argv,
1054                                     RTEMS_SHELL_MAXIMUM_ARGUMENTS)) {
1055            int exit_code = rtems_shell_execute_cmd(argv[0], argc, argv);
1056            if (shell_env->exit_code != NULL)
1057              *shell_env->exit_code = exit_code;
1058            if (exit_code != 0 && shell_env->exit_on_error)
1059              shell_env->exit_shell = true;
1060          }
1061
1062          /* end exec cmd section */
1063          if (shell_env->exit_shell)
1064            break;
1065        }
1066
1067        fflush( stdout );
1068        fflush( stderr );
1069      }
1070      shell_std_debug("end: %d %d\n", result, shell_env->forever);
1071    } while (result && shell_env->forever);
1072
1073  }
1074
1075  if (cmds[0])
1076    free (cmds[0]);
1077  if (cmd_argv)
1078    free (cmd_argv);
1079  if (prompt)
1080    free (prompt);
1081
1082  shell_std_debug("child in-to-close: %p\n", stdinToClose);
1083  shell_std_debug("child out-to-close: %p\n", stdoutToClose);
1084
1085  if (stdinToClose) {
1086    fclose( stdinToClose );
1087  } else {
1088    if (tcsetattr(fileno(stdin), TCSADRAIN, &previous_term) < 0) {
1089      fprintf(
1090        stderr,
1091        "shell: cannot reset terminal attributes (%s)\n",
1092        shell_env->devname
1093      );
1094    }
1095  }
1096  if ( stdoutToClose )
1097    fclose( stdoutToClose );
1098  rtems_shell_clear_shell_env();
1099  return result;
1100}
1101
1102/* ----------------------------------------------- */
1103static rtems_status_code rtems_shell_run (
1104  const char *task_name,
1105  size_t task_stacksize,
1106  rtems_task_priority task_priority,
1107  const char *devname,
1108  bool forever,
1109  bool wait,
1110  const char *input,
1111  const char *output,
1112  bool output_append,
1113  rtems_id wake_on_end,
1114  int *exit_code,
1115  bool echo,
1116  rtems_shell_login_check_t login_check
1117)
1118{
1119  rtems_id           task_id;
1120  rtems_status_code  sc;
1121  rtems_shell_env_t *shell_env;
1122  rtems_name         name;
1123
1124  rtems_shell_init_environment();
1125
1126  if ( task_name && strlen(task_name) >= 4)
1127    name = rtems_build_name(
1128      task_name[0], task_name[1], task_name[2], task_name[3]);
1129  else
1130    name = rtems_build_name( 'S', 'E', 'N', 'V' );
1131
1132  sc = rtems_task_create(
1133    name,
1134    task_priority,
1135    task_stacksize,
1136    RTEMS_PREEMPT | RTEMS_TIMESLICE | RTEMS_NO_ASR,
1137    RTEMS_LOCAL | RTEMS_FLOATING_POINT,
1138    &task_id
1139  );
1140  if (sc != RTEMS_SUCCESSFUL) {
1141    rtems_error(sc,"creating task %s in shell_init()",task_name);
1142    return sc;
1143  }
1144
1145  shell_env = rtems_shell_init_env( NULL );
1146  if ( !shell_env )  {
1147   rtems_error(RTEMS_NO_MEMORY,
1148               "allocating shell_env %s in shell_init()",task_name);
1149   return RTEMS_NO_MEMORY;
1150  }
1151
1152  shell_std_debug("run: env: %p\n", shell_env);
1153
1154  shell_env->devname       = devname;
1155  shell_env->taskname      = task_name;
1156
1157  shell_env->exit_shell    = false;
1158  shell_env->forever       = forever;
1159  shell_env->echo          = echo;
1160  shell_env->input         = input == NULL ? NULL : strdup (input);
1161  shell_env->output        = output == NULL ? NULL : strdup (output);
1162  shell_env->output_append = output_append;
1163  shell_env->parent_stdin  = stdin;
1164  shell_env->parent_stdout = stdout;
1165  shell_env->parent_stderr = stderr;
1166  shell_env->wake_on_end   = wake_on_end;
1167  shell_env->exit_code     = exit_code;
1168  shell_env->login_check   = login_check;
1169  shell_env->uid           = getuid();
1170  shell_env->gid           = getgid();
1171
1172  getcwd(shell_env->cwd, sizeof(shell_env->cwd));
1173
1174  shell_std_debug("run out: %d (%p)\n",
1175                  fileno(shell_env->parent_stdout), shell_env->parent_stdout);
1176  shell_std_debug("run  in: %d (%p)\n",
1177                  fileno(shell_env->parent_stdin), shell_env->parent_stdin);
1178
1179  sc = rtems_task_start(task_id, rtems_shell_task,
1180                        (rtems_task_argument) shell_env);
1181  if (sc != RTEMS_SUCCESSFUL) {
1182    rtems_error(sc,"starting task %s in shell_init()",task_name);
1183    free( (void*) shell_env->input );
1184    free( (void*) shell_env->output );
1185    free( shell_env );
1186    return sc;
1187  }
1188
1189  if (wait) {
1190    rtems_event_set out;
1191    sc = rtems_event_receive (RTEMS_EVENT_1, RTEMS_WAIT, 0, &out);
1192  }
1193
1194  shell_std_debug("run: end: sc:%d ec:%d\n", sc, *exit_code);
1195
1196  return sc;
1197}
1198
1199rtems_status_code rtems_shell_init(
1200  const char *task_name,
1201  size_t task_stacksize,
1202  rtems_task_priority task_priority,
1203  const char *devname,
1204  bool forever,
1205  bool wait,
1206  rtems_shell_login_check_t login_check
1207)
1208{
1209  rtems_id to_wake = RTEMS_ID_NONE;
1210  int exit_code = 0;
1211
1212  if ( wait )
1213    to_wake = rtems_task_self();
1214
1215  return rtems_shell_run(
1216    task_name,               /* task_name */
1217    task_stacksize,          /* task_stacksize */
1218    task_priority,           /* task_priority */
1219    devname,                 /* devname */
1220    forever,                 /* forever */
1221    wait,                    /* wait */
1222    "stdin",                 /* input */
1223    "stdout",                /* output */
1224    false,                   /* output_append */
1225    to_wake,                 /* wake_on_end */
1226    &exit_code,              /* exit code of command */
1227    false,                   /* echo */
1228    login_check              /* login check */
1229  );
1230}
1231
1232rtems_status_code rtems_shell_script (
1233  const char          *task_name,
1234  size_t               task_stacksize,
1235  rtems_task_priority  task_priority,
1236  const char*          input,
1237  const char*          output,
1238  bool                 output_append,
1239  bool                 wait,
1240  bool                 echo
1241)
1242{
1243  rtems_id to_wake = RTEMS_ID_NONE;
1244  int exit_code = 0;
1245  rtems_status_code sc;
1246
1247  shell_std_debug("script: in: %s out: %s\n", input, output);
1248
1249  if ( wait )
1250    to_wake = rtems_task_self();
1251
1252  sc = rtems_shell_run(
1253    task_name,       /* task_name */
1254    task_stacksize,  /* task_stacksize */
1255    task_priority,   /* task_priority */
1256    NULL,            /* devname */
1257    0,               /* forever */
1258    wait,            /* wait */
1259    input,           /* input */
1260    output,          /* output */
1261    output_append,   /* output_append */
1262    to_wake,         /* wake_on_end */
1263    &exit_code,      /* exit_code */
1264    echo,            /* echo */
1265    NULL             /* login check */
1266  );
1267
1268  if (sc == RTEMS_SUCCESSFUL)
1269  {
1270    /* Place holder until RTEMS 5 is released then the interface for
1271     * this call will change. */
1272  }
1273
1274  shell_std_debug("script: end: %d\n", sc);
1275
1276  return sc;
1277}
Note: See TracBrowser for help on using the repository browser.