source: rtems/cpukit/libmisc/shell/shell.c @ 1af8e45b

5
Last change on this file since 1af8e45b 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
RevLine 
[dd74e612]1/*
2 *
3 *  Instantatiate a new terminal shell.
4 *
[aed742c]5 *  Author:
[dd74e612]6 *
[aed742c]7 *   WORK: fernando.ruiz@ctv.es
[dd74e612]8 *   HOME: correo@fernando-ruiz.com
9 *
[4e5299f]10 *  The license and distribution terms for this file may be
11 *  found in the file LICENSE in this distribution or at
[c499856]12 *  http://www.rtems.org/license/LICENSE.
[dd74e612]13 */
[814d9588]14
[b2712e3]15#ifdef HAVE_CONFIG_H
16#include "config.h"
17#endif
[dd74e612]18
19#include <stdio.h>
[84206479]20#include <time.h>
[dd74e612]21
22#include <rtems.h>
[d007cc2]23#include <rtems/error.h>
[dd74e612]24#include <rtems/libio.h>
[aed742c]25#include <rtems/libio_.h>
[b2712e3]26#include <rtems/shell.h>
[4e5299f]27#include <rtems/shellconfig.h>
[06f8e558]28#include <rtems/console.h>
[4e5299f]29#include "internal.h"
[dd74e612]30
31#include <termios.h>
32#include <string.h>
33#include <stdlib.h>
34#include <ctype.h>
[70d689a]35#include <sys/stat.h>
[dd74e612]36#include <unistd.h>
37#include <errno.h>
[b2712e3]38#include <pwd.h>
[6cd4a5c]39#include <pthread.h>
40#include <assert.h>
[dd74e612]41
[d007cc2]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
[5f0ab5cf]52const rtems_shell_env_t rtems_global_shell_env = {
[06f8e558]53  .magic         = rtems_build_name('S', 'E', 'N', 'V'),
[d007cc2]54  .managed       = false,
[06f8e558]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,
[d007cc2]64  .parent_stdin  = NULL,
65  .parent_stdout = NULL,
66  .parent_stderr = NULL,
[06f8e558]67  .wake_on_end   = RTEMS_ID_NONE,
[d007cc2]68  .exit_code     = NULL,
69  .login_check   = NULL,
70  .uid           = 0,
71  .gid           = 0
[06f8e558]72};
73
[d007cc2]74typedef struct rtems_shell_env_key_handle
75{
76  bool managed;
77  rtems_shell_env_t* env;
78} rtems_shell_env_key_handle;
79
[373ccbb9]80static pthread_once_t rtems_shell_once = PTHREAD_ONCE_INIT;
81
[6cd4a5c]82static pthread_key_t rtems_shell_current_env_key;
[4e5299f]83
84/*
[814d9588]85 *  Initialize the shell user/process environment information
[4e5299f]86 */
[94547d1]87static rtems_shell_env_t *rtems_shell_init_env(
[d007cc2]88  rtems_shell_env_t *shell_env_parent
[4e5299f]89)
90{
[13c37ad]91  rtems_shell_env_t *shell_env;
92
93  shell_env = malloc(sizeof(rtems_shell_env_t));
94  if ( !shell_env )
95    return NULL;
[d007cc2]96
97  if ( shell_env_parent == NULL ) {
98    shell_env_parent = rtems_shell_get_current_env();
99  }
100  if ( shell_env_parent == NULL ) {
[1ff9922]101    *shell_env = rtems_global_shell_env;
[13c37ad]102  } else {
[d007cc2]103    *shell_env = *shell_env_parent;
[4e5299f]104  }
[d007cc2]105  shell_env->managed = true;
106  shell_env->taskname = NULL;
[4e5299f]107
[814d9588]108  return shell_env;
[dd74e612]109}
[4e5299f]110
[798ff5a]111/*
112 *  Completely free a shell_env_t and all associated memory
113 */
[94547d1]114static void rtems_shell_env_free(
[798ff5a]115  void *ptr
116)
117{
[d007cc2]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    }
[798ff5a]129
[d007cc2]130    free( handle );
131  }
[798ff5a]132}
133
[373ccbb9]134static void rtems_shell_create_file(const char *name, const char *content)
[6cd4a5c]135{
[373ccbb9]136  FILE *fp = fopen(name, "wx");
137
138  if (fp != NULL) {
139    fputs(content, fp);
140    fclose(fp);
141  }
142}
143
[7840b66]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
[373ccbb9]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");
[7840b66]177
178  rtems_shell_init_commands();
[2465c01]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);
[6cd4a5c]185}
186
[d007cc2]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
[6cd4a5c]245/*
246 *  Return the current shell environment
247 */
248rtems_shell_env_t *rtems_shell_get_current_env(void)
249{
[d007cc2]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;
[bb58190]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();
[d007cc2]263  if (env != NULL) {
264    shell_std_debug("dup: existing parent\n");
[bb58190]265    *copy = *env;
266  }
267  else {
[d007cc2]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);
[bb58190]279  }
[d007cc2]280  /*
281   * Duplicated environments are not managed.
282   */
283  copy->managed = false;
[6cd4a5c]284}
285
[814d9588]286/*
287 *  Get a line of user input with modest features
288 */
[94547d1]289static int rtems_shell_line_editor(
[ea90df23]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;
[7c4cdeb9]299  int          c;
[ea90df23]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;
[d007cc2]308  int          in_fileno = fileno(in);
309  int          out_fileno = fileno(out);
[8a775c27]310
[d007cc2]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));
[ea90df23]317
318  col = last_col = 0;
[8a775c27]319
[d007cc2]320  tcdrain(in_fileno);
[ea90df23]321  if (out)
[d007cc2]322    tcdrain(out_fileno);
[ea90df23]323
324  if (output && prompt)
325    fprintf(out, "\r%s", prompt);
[8a775c27]326
[ea90df23]327  line[0] = 0;
328  new_line[0] = 0;
[8a775c27]329
[ea90df23]330  for (;;) {
[8a775c27]331
[ea90df23]332    if (output)
333      fflush(out);
334
335    extended_key = rtems_shell_getchar(in);
336
337    if (extended_key == EOF)
338      return -2;
[8a775c27]339
[ea90df23]340    c = extended_key & RTEMS_SHELL_KEYS_NORMAL_MASK;
[8a775c27]341
[ea90df23]342    /*
343     * Make the extended_key usable as a boolean.
344     */
345    extended_key &= ~RTEMS_SHELL_KEYS_NORMAL_MASK;
[8a775c27]346
[ea90df23]347    up = 0;
[8a775c27]348
[ea90df23]349    if (extended_key)
350    {
351      switch (c)
352      {
353        case RTEMS_SHELL_KEYS_END:
354          if (output)
[8bca4fc]355            fprintf(out, "%s", line + col);
[ea90df23]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:
[22e98377]368          c = 2;
369          extended_key = 0;
[ea90df23]370          break;
371
[22e98377]372        case RTEMS_SHELL_KEYS_RARROW:
373          c = 6;
374          extended_key = 0;
375          break;
[ea90df23]376
377        case RTEMS_SHELL_KEYS_UARROW:
[22e98377]378          c = 16;
379          extended_key = 0;
380          break;
[ea90df23]381
382        case RTEMS_SHELL_KEYS_DARROW:
[22e98377]383          c = 14;
384          extended_key = 0;
385          break;
[ea90df23]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    }
[22e98377]407    if (!extended_key)
[ea90df23]408    {
409      switch (c)
410      {
[22e98377]411        case 1:                         /*Control-a*/
[ea90df23]412          if (output) {
413            if (prompt)
414              fprintf(out,"\r%s", prompt);
415          }
416          col = 0;
417          break;
[8a775c27]418
[22e98377]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*/
[ea90df23]449          if (output)
[8bca4fc]450            fprintf(out, "%s", line + col);
[ea90df23]451          col = (int) strlen (line);
452          break;
453
[22e98377]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) {
[06ac8b71]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), ' ');
[22e98377]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*/
[ea90df23]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;
[8a775c27]488
[ea90df23]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");
[8a775c27]524
[ea90df23]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;
[22e98377]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              }
[ea90df23]543            }
544          }
545        }
546        return cmd;
547
[22e98377]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
[ea90df23]630        default:
[7311863]631          if ((col < (size - 1)) && (c >= ' ') && (c <= '~')) {
[ea90df23]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) {
[8bca4fc]638                fprintf(out, "%s", line + col);
[ea90df23]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
[fa028bb]656static bool rtems_shell_login(rtems_shell_env_t *env, FILE * in,FILE * out)
657{
[d007cc2]658  FILE  *fd;
659  int    c;
660  time_t t;
[bb58190]661
[6dd411aa]662  if (out) {
[bb58190]663    if ((env->devname[5]!='p')||
664        (env->devname[6]!='t')||
665        (env->devname[7]!='y')) {
[6dd411aa]666      fd = fopen("/etc/issue","r");
667      if (fd) {
[d007cc2]668        while ((c = fgetc(fd)) != EOF) {
[6dd411aa]669          if (c=='@')  {
[d007cc2]670            switch (c = fgetc(fd)) {
[6dd411aa]671              case 'L':
[bb58190]672                fprintf(out,"%s", env->devname);
[6dd411aa]673                break;
674              case 'B':
675                fprintf(out,"0");
676                break;
[70d689a]677              case 'T':
[6dd411aa]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':
[1af8e45b]686                fprintf(
687                  out,
688                  "%s\n%s",
689                  rtems_get_version_string(),
690                  rtems_get_copyright_notice()
691                );
[6dd411aa]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':
[bb58190]724                fprintf(out,"%s", env->devname);
[6dd411aa]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':
[4b9b6dd]743                fprintf(out,rtems_get_version_string());
[6dd411aa]744                break;
745              case 'v':
[1af8e45b]746                fprintf(
747                  out,
748                  "%s\n%s",
749                  rtems_get_version_string(),
750                  rtems_get_copyright_notice()
751                );
[6dd411aa]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
[bb58190]768  return rtems_shell_login_prompt(in, out, env->devname, env->login_check);
[b2712e3]769}
770
[814d9588]771#if defined(SHELL_DEBUG)
[2eeb648c]772void rtems_shell_print_env(
773  rtems_shell_env_t * shell_env
[6dd411aa]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
[dd74e612]794
[94547d1]795static rtems_task rtems_shell_task(rtems_task_argument task_argument)
[6dd411aa]796{
[1ff9922]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);
[f004b2b8]802  rtems_task_exit();
[6dd411aa]803}
[dd74e612]804
[fa028bb]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) {
[d007cc2]812    rtems_error(sc, "rtems_libio_set_private_env():");
[fa028bb]813    return false;
814  }
815
816  /* Make an effective root user */
817  seteuid(0);
818  setegid(0);
819
820  return chroot("/") == 0;
821}
822
[ea90df23]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)
[aed742c]827
[1167235]828bool rtems_shell_main_loop(
[d007cc2]829  rtems_shell_env_t *shell_env
[798ff5a]830)
[6dd411aa]831{
[d007cc2]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;
[2eeb648c]846
[2465c01]847  rtems_shell_init_environment();
[7840b66]848
[d007cc2]849  if (shell_env->magic != rtems_build_name('S', 'E', 'N', 'V')) {
850    rtems_error(0, "invalid shell environment passed to the main loop)");
[6cd4a5c]851    return false;
852  }
853
[d007cc2]854  if (!rtems_shell_set_shell_env(shell_env))
[1167235]855    return false;
[dd74e612]856
[fa028bb]857  if (!rtems_shell_init_user_env()) {
[d007cc2]858    rtems_error(0, "rtems_shell_init_user_env");
859    rtems_shell_clear_shell_env();
[fa028bb]860    return false;
861  }
[6dd411aa]862
[d007cc2]863  shell_std_debug("env: %p\n", shell_env);
[be8ab6a]864
[d007cc2]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
[1ff9922]873      stdout = stderr;
[d007cc2]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;
[1ff9922]884    }
[d007cc2]885    stdout = output;
886    stdoutToClose = output;
[1ff9922]887  }
[8a775c27]888
[d007cc2]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) {
[1ff9922]895      fprintf(stderr, "shell: open input %s failed: %s\n",
[d007cc2]896              shell_env->input, strerror(errno));
897      if (stdoutToClose != NULL)
898        fclose(stdoutToClose);
899      rtems_shell_clear_shell_env();
[1167235]900      return false;
[1ff9922]901    }
902    stdin = input;
[a3ddb08b]903    stdinToClose = input;
[1167235]904    shell_env->forever = false;
[d007cc2]905    input_file = true;
[1ff9922]906  }
[d007cc2]907
908  if (!input_file) {
909    /* Make a raw terminal, Linux Manuals */
[8084ce80]910    if (tcgetattr(fileno(stdin), &previous_term) >= 0) {
911      term = previous_term;
[1ff9922]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);
[d007cc2]916      term.c_cflag |= CLOCAL | CREAD;
[1ff9922]917      term.c_cc[VMIN]  = 1;
918      term.c_cc[VTIME] = 0;
919      if (tcsetattr (fileno(stdin), TCSADRAIN, &term) < 0) {
920        fprintf(stderr,
[d007cc2]921                "shell: cannot set terminal attributes(%s)\n",shell_env->devname);
[1ff9922]922      }
[814d9588]923    }
[ea90df23]924    cmd_count = RTEMS_SHELL_CMD_COUNT;
925    prompt = malloc(RTEMS_SHELL_PROMPT_SIZE);
926    if (!prompt)
927        fprintf(stderr,
[d007cc2]928                "shell: cannot allocate prompt memory\n");
[6dd411aa]929  }
930
[d007cc2]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);
[1ff9922]938
[ea90df23]939  /*
940   * Allocate the command line buffers.
941   */
[8c422e2]942  cmd_argv = malloc (RTEMS_SHELL_CMD_SIZE);
943  if (!cmd_argv) {
944    fprintf(stderr, "no memory for command line buffers\n" );
945  }
[8a775c27]946
[ea90df23]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  }
[8c422e2]951
952  if (cmd_argv && cmds[0]) {
[ea90df23]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;
[6dd411aa]958    }
[8a775c27]959
[ea90df23]960    do {
[fa028bb]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 {
[ffd5285]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
[fa028bb]978          result = true;
979        }
[7c4cdeb9]980      }
981
982      if (result)  {
[ea90df23]983        const char *c;
984        memset (cmds[0], 0, cmd_count * RTEMS_SHELL_CMD_SIZE);
[1ff9922]985        if (!input_file) {
[ea90df23]986          rtems_shell_cat_file(stdout,"/etc/motd");
987          fprintf(stdout, "\n"
[a2097c5]988                  "RTEMS Shell on %s. Use 'help' to list commands.\n",
[ea90df23]989                  shell_env->devname);
[1ff9922]990        }
[55c64fc9]991
992        if (input_file)
993          chdir(shell_env->cwd);
994        else
995          chdir("/"); /* XXX: chdir to getpwent homedir */
[8a775c27]996
[1167235]997        shell_env->exit_shell = false;
[ea90df23]998
999        for (;;) {
1000          /* Prompt section */
1001          if (prompt) {
1002            rtems_shell_get_prompt(shell_env, prompt,
1003                                   RTEMS_SHELL_PROMPT_SIZE);
1004          }
[8a775c27]1005
[ea90df23]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 */
[8a775c27]1013
[d007cc2]1014          if (cmd == -2) {
1015            result = false;
[ea90df23]1016            break; /*EOF*/
[d007cc2]1017          }
[ea90df23]1018
1019          line++;
1020
[55c64fc9]1021          if (shell_env->echo)
1022            fprintf(stdout, "%d: %s\n", line, cmds[cmd]);
[8a775c27]1023
[ea90df23]1024          /* evaluate cmd section */
1025          c = cmds[cmd];
1026          while (*c) {
[bab5c5fa]1027            if (!isblank((unsigned char)*c))
[ea90df23]1028              break;
1029            c++;
1030          }
[1ff9922]1031
[ea90df23]1032          if (*c == '\0')   /* empty line */
1033            continue;
[a5de1ef]1034
[ea90df23]1035          if (*c == '#') {  /* comment character */
1036            cmds[cmd][0] = 0;
1037            continue;
1038          }
[1ff9922]1039
[ea90df23]1040          if (!strcmp(cmds[cmd],"bye") || !strcmp(cmds[cmd],"exit")) {
1041            fprintf(stdout, "Shell exiting\n" );
1042            break;
1043          }
[6dd411aa]1044
[ea90df23]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           */
[8c422e2]1052          memcpy (cmd_argv, cmds[cmd], RTEMS_SHELL_CMD_SIZE);
1053          if (!rtems_shell_make_args(cmd_argv, &argc, argv,
[ea90df23]1054                                     RTEMS_SHELL_MAXIMUM_ARGUMENTS)) {
[d007cc2]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;
[6dd411aa]1060          }
[ea90df23]1061
1062          /* end exec cmd section */
1063          if (shell_env->exit_shell)
1064            break;
[6dd411aa]1065        }
1066
[ea90df23]1067        fflush( stdout );
1068        fflush( stderr );
[6dd411aa]1069      }
[d007cc2]1070      shell_std_debug("end: %d %d\n", result, shell_env->forever);
[ea90df23]1071    } while (result && shell_env->forever);
1072
1073  }
1074
[8c422e2]1075  if (cmds[0])
1076    free (cmds[0]);
1077  if (cmd_argv)
1078    free (cmd_argv);
[798ff5a]1079  if (prompt)
1080    free (prompt);
1081
[d007cc2]1082  shell_std_debug("child in-to-close: %p\n", stdinToClose);
1083  shell_std_debug("child out-to-close: %p\n", stdoutToClose);
1084
[8084ce80]1085  if (stdinToClose) {
[a3ddb08b]1086    fclose( stdinToClose );
[8084ce80]1087  } else {
[798ff5a]1088    if (tcsetattr(fileno(stdin), TCSADRAIN, &previous_term) < 0) {
[8084ce80]1089      fprintf(
1090        stderr,
1091        "shell: cannot reset terminal attributes (%s)\n",
1092        shell_env->devname
1093      );
1094    }
1095  }
[a3ddb08b]1096  if ( stdoutToClose )
1097    fclose( stdoutToClose );
[d007cc2]1098  rtems_shell_clear_shell_env();
[1ff9922]1099  return result;
[dd74e612]1100}
[6dd411aa]1101
[dd74e612]1102/* ----------------------------------------------- */
[cbd1e87]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,
[d007cc2]1114  int *exit_code,
[cbd1e87]1115  bool echo,
1116  rtems_shell_login_check_t login_check
[6dd411aa]1117)
1118{
1119  rtems_id           task_id;
1120  rtems_status_code  sc;
[2eeb648c]1121  rtems_shell_env_t *shell_env;
[4e5299f]1122  rtems_name         name;
1123
[d007cc2]1124  rtems_shell_init_environment();
1125
[e41eaa88]1126  if ( task_name && strlen(task_name) >= 4)
[814d9588]1127    name = rtems_build_name(
1128      task_name[0], task_name[1], task_name[2], task_name[3]);
[4e5299f]1129  else
1130    name = rtems_build_name( 'S', 'E', 'N', 'V' );
[6dd411aa]1131
1132  sc = rtems_task_create(
[4e5299f]1133    name,
[6dd411aa]1134    task_priority,
[4e5299f]1135    task_stacksize,
[3899a537]1136    RTEMS_PREEMPT | RTEMS_TIMESLICE | RTEMS_NO_ASR,
[6dd411aa]1137    RTEMS_LOCAL | RTEMS_FLOATING_POINT,
1138    &task_id
1139  );
1140  if (sc != RTEMS_SUCCESSFUL) {
[d007cc2]1141    rtems_error(sc,"creating task %s in shell_init()",task_name);
[6dd411aa]1142    return sc;
1143  }
1144
[2eeb648c]1145  shell_env = rtems_shell_init_env( NULL );
[6dd411aa]1146  if ( !shell_env )  {
[d007cc2]1147   rtems_error(RTEMS_NO_MEMORY,
1148               "allocating shell_env %s in shell_init()",task_name);
[6dd411aa]1149   return RTEMS_NO_MEMORY;
1150  }
[d007cc2]1151
1152  shell_std_debug("run: env: %p\n", shell_env);
1153
[1ff9922]1154  shell_env->devname       = devname;
1155  shell_env->taskname      = task_name;
[d007cc2]1156
[1167235]1157  shell_env->exit_shell    = false;
[1ff9922]1158  shell_env->forever       = forever;
[55c64fc9]1159  shell_env->echo          = echo;
[d007cc2]1160  shell_env->input         = input == NULL ? NULL : strdup (input);
1161  shell_env->output        = output == NULL ? NULL : strdup (output);
[1ff9922]1162  shell_env->output_append = output_append;
[d007cc2]1163  shell_env->parent_stdin  = stdin;
1164  shell_env->parent_stdout = stdout;
1165  shell_env->parent_stderr = stderr;
[1ff9922]1166  shell_env->wake_on_end   = wake_on_end;
[d007cc2]1167  shell_env->exit_code     = exit_code;
[8a775c27]1168  shell_env->login_check   = login_check;
[ffd5285]1169  shell_env->uid           = getuid();
1170  shell_env->gid           = getgid();
[6dd411aa]1171
[55c64fc9]1172  getcwd(shell_env->cwd, sizeof(shell_env->cwd));
1173
[d007cc2]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
[798ff5a]1179  sc = rtems_task_start(task_id, rtems_shell_task,
[d007cc2]1180                        (rtems_task_argument) shell_env);
[798ff5a]1181  if (sc != RTEMS_SUCCESSFUL) {
[d007cc2]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 );
[798ff5a]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
[d007cc2]1194  shell_std_debug("run: end: sc:%d ec:%d\n", sc, *exit_code);
1195
1196  return sc;
[6dd411aa]1197}
[1ff9922]1198
[798ff5a]1199rtems_status_code rtems_shell_init(
[cbd1e87]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
[1ff9922]1207)
1208{
[06f8e558]1209  rtems_id to_wake = RTEMS_ID_NONE;
[d007cc2]1210  int exit_code = 0;
[798ff5a]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 */
[06f8e558]1224    false,                   /* output_append */
[798ff5a]1225    to_wake,                 /* wake_on_end */
[d007cc2]1226    &exit_code,              /* exit code of command */
[06f8e558]1227    false,                   /* echo */
[8a775c27]1228    login_check              /* login check */
[798ff5a]1229  );
[1ff9922]1230}
1231
[d007cc2]1232rtems_status_code rtems_shell_script (
[e41eaa88]1233  const char          *task_name,
[06f8e558]1234  size_t               task_stacksize,
[1ff9922]1235  rtems_task_priority  task_priority,
1236  const char*          input,
1237  const char*          output,
[06f8e558]1238  bool                 output_append,
1239  bool                 wait,
1240  bool                 echo
[1ff9922]1241)
1242{
[d007cc2]1243  rtems_id to_wake = RTEMS_ID_NONE;
1244  int exit_code = 0;
[1ff9922]1245  rtems_status_code sc;
1246
[d007cc2]1247  shell_std_debug("script: in: %s out: %s\n", input, output);
1248
1249  if ( wait )
1250    to_wake = rtems_task_self();
[8a775c27]1251
[798ff5a]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 */
[d007cc2]1262    to_wake,         /* wake_on_end */
1263    &exit_code,      /* exit_code */
[06f8e558]1264    echo,            /* echo */
[8a775c27]1265    NULL             /* login check */
[798ff5a]1266  );
[d007cc2]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);
[1ff9922]1275
1276  return sc;
1277}
Note: See TracBrowser for help on using the repository browser.