[PATCH v2] libmisc/shell: Fix the handling of joel scripts in telnet

chrisj at rtems.org chrisj at rtems.org
Tue Apr 14 08:46:59 UTC 2020


From: Chris Johns <chrisj at rtems.org>

- 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
---
 cpukit/include/rtems/shell.h       |   7 +-
 cpukit/libmisc/shell/shell.c       | 369 +++++++++++++++++++++--------
 testsuites/libtests/shell01/init.c | 123 +++++++++-
 3 files changed, 394 insertions(+), 105 deletions(-)

diff --git a/cpukit/include/rtems/shell.h b/cpukit/include/rtems/shell.h
index 973db16605..d705dcb0a7 100644
--- a/cpukit/include/rtems/shell.h
+++ b/cpukit/include/rtems/shell.h
@@ -220,16 +220,21 @@ extern rtems_status_code rtems_shell_script(
 typedef struct {
   /** 'S','E','N','V': Shell Environment */
   rtems_name magic;
+  bool managed;
   const char *devname;
   const char *taskname;
   bool exit_shell; /* logout */
   bool forever; /* repeat login */
-  int errorlevel;
+  int *exit_code;
+  bool exit_on_error;
   bool echo;
   char cwd[256];
   const char *input;
   const char *output;
   bool output_append;
+  FILE *parent_stdin;
+  FILE *parent_stdout;
+  FILE *parent_stderr;
   rtems_id wake_on_end;
   rtems_shell_login_check_t login_check;
 
diff --git a/cpukit/libmisc/shell/shell.c b/cpukit/libmisc/shell/shell.c
index c2ea3c4afa..f00ae54b49 100644
--- a/cpukit/libmisc/shell/shell.c
+++ b/cpukit/libmisc/shell/shell.c
@@ -20,6 +20,7 @@
 #include <time.h>
 
 #include <rtems.h>
+#include <rtems/error.h>
 #include <rtems/libio.h>
 #include <rtems/libio_.h>
 #include <rtems/shell.h>
@@ -38,22 +39,44 @@
 #include <pthread.h>
 #include <assert.h>
 
+#define SHELL_STD_DEBUG 0
+
+#if SHELL_STD_DEBUG
+#include <rtems/bspIo.h>
+#define shell_std_debug(...) \
+  do { printk("shell[%08x]: ", rtems_task_self()); printk(__VA_ARGS__); } while (0)
+#else
+#define shell_std_debug(...)
+#endif
+
 const rtems_shell_env_t rtems_global_shell_env = {
   .magic         = rtems_build_name('S', 'E', 'N', 'V'),
+  .managed       = false,
   .devname       = CONSOLE_DEVICE_NAME,
   .taskname      = "SHGL",
   .exit_shell    = false,
   .forever       = true,
-  .errorlevel    = -1,
   .echo          = false,
   .cwd           = "/",
   .input         = NULL,
   .output        = NULL,
   .output_append = false,
+  .parent_stdin  = NULL,
+  .parent_stdout = NULL,
+  .parent_stderr = NULL,
   .wake_on_end   = RTEMS_ID_NONE,
-  .login_check   = NULL
+  .exit_code     = NULL,
+  .login_check   = NULL,
+  .uid           = 0,
+  .gid           = 0
 };
 
+typedef struct rtems_shell_env_key_handle
+{
+  bool managed;
+  rtems_shell_env_t* env;
+} rtems_shell_env_key_handle;
+
 static pthread_once_t rtems_shell_once = PTHREAD_ONCE_INIT;
 
 static pthread_key_t rtems_shell_current_env_key;
@@ -62,7 +85,7 @@ static pthread_key_t rtems_shell_current_env_key;
  *  Initialize the shell user/process environment information
  */
 static rtems_shell_env_t *rtems_shell_init_env(
-  rtems_shell_env_t *shell_env_p
+  rtems_shell_env_t *shell_env_parent
 )
 {
   rtems_shell_env_t *shell_env;
@@ -70,12 +93,17 @@ static rtems_shell_env_t *rtems_shell_init_env(
   shell_env = malloc(sizeof(rtems_shell_env_t));
   if ( !shell_env )
     return NULL;
-  if ( !shell_env_p ) {
+
+  if ( shell_env_parent == NULL ) {
+    shell_env_parent = rtems_shell_get_current_env();
+  }
+  if ( shell_env_parent == NULL ) {
     *shell_env = rtems_global_shell_env;
-    shell_env->taskname = NULL;
   } else {
-    *shell_env = *shell_env_p;
+    *shell_env = *shell_env_parent;
   }
+  shell_env->managed = true;
+  shell_env->taskname = NULL;
 
   return shell_env;
 }
@@ -87,17 +115,20 @@ static void rtems_shell_env_free(
   void *ptr
 )
 {
-  rtems_shell_env_t *shell_env;
-  shell_env = (rtems_shell_env_t *) ptr;
-
-  if ( !ptr )
-    return;
+  if ( ptr != NULL ) {
+    rtems_shell_env_key_handle *handle = (rtems_shell_env_key_handle *) ptr;
+    rtems_shell_env_t *shell_env = handle->env;
+
+    if ( handle->managed ) {
+      if ( shell_env->input )
+        free((void *)shell_env->input);
+      if ( shell_env->output )
+        free((void *)shell_env->output);
+      free( shell_env );
+    }
 
-  if ( shell_env->input )
-    free((void *)shell_env->input);
-  if ( shell_env->output )
-    free((void *)shell_env->output);
-  free( ptr );
+    free( handle );
+  }
 }
 
 static void rtems_shell_create_file(const char *name, const char *content)
@@ -153,12 +184,73 @@ void rtems_shell_init_environment(void)
   assert(pthread_once(&rtems_shell_once, rtems_shell_init_once) == 0);
 }
 
+/*
+ * Set the shell env into the current thread's shell key.
+ */
+static bool rtems_shell_set_shell_env(
+  rtems_shell_env_t* shell_env
+)
+{
+  /*
+   * The shell environment can be managed or it can be provided by a
+   * user. We need to create a handle to hold the env pointer.
+   */
+  rtems_shell_env_key_handle *handle;
+  int eno;
+
+  handle = malloc(sizeof(rtems_shell_env_key_handle));
+  if (handle == NULL) {
+    rtems_error(0, "no memory for shell env key handle)");
+    return false;
+  }
+
+  handle->managed = shell_env->managed;
+  handle->env = shell_env;
+
+  eno = pthread_setspecific(rtems_shell_current_env_key, handle);
+  if (eno != 0) {
+    rtems_error(0, "pthread_setspecific(shell_current_env_key): set");
+    return false;
+  }
+
+  return true;
+}
+
+/*
+ * Clear the current thread's shell key.
+ */
+static void rtems_shell_clear_shell_env(void)
+{
+  int eno;
+
+  /*
+   * Run the destructor manually.
+   */
+  rtems_shell_env_free(pthread_getspecific(rtems_shell_current_env_key));
+
+  /*
+   * Clear the key
+   */
+  eno = pthread_setspecific(rtems_shell_current_env_key, NULL);
+  if (eno != 0)
+    rtems_error(0, "pthread_setspecific(shell_current_env_key): clear");
+
+  /*
+   * Clear stdin and stdout file pointers of they will be closed
+   */
+  stdin = NULL;
+  stdout = NULL;
+}
+
 /*
  *  Return the current shell environment
  */
 rtems_shell_env_t *rtems_shell_get_current_env(void)
 {
-  return (rtems_shell_env_t *) pthread_getspecific(rtems_shell_current_env_key);
+  rtems_shell_env_key_handle *handle;
+  handle = (rtems_shell_env_key_handle*)
+    pthread_getspecific(rtems_shell_current_env_key);
+  return handle == NULL ? NULL : handle->env;
 }
 
 /*
@@ -168,15 +260,27 @@ rtems_shell_env_t *rtems_shell_get_current_env(void)
 void rtems_shell_dup_current_env(rtems_shell_env_t *copy)
 {
   rtems_shell_env_t *env = rtems_shell_get_current_env();
-  if (env) {
+  if (env != NULL) {
+    shell_std_debug("dup: existing parent\n");
     *copy = *env;
   }
   else {
-    memset(copy, 0, sizeof(rtems_shell_env_t));
-    copy->magic    = rtems_build_name('S', 'E', 'N', 'V');
-    copy->devname  = CONSOLE_DEVICE_NAME;
-    copy->taskname = "RTSH";
+    *copy = rtems_global_shell_env;
+    copy->magic         = rtems_build_name('S', 'E', 'N', 'V');
+    copy->devname       = CONSOLE_DEVICE_NAME;
+    copy->taskname      = "RTSH";
+    copy->parent_stdout = stdout;
+    copy->parent_stdin  = stdin;
+    copy->parent_stderr = stderr;
+    shell_std_debug("dup: global: copy: %p out: %d (%p) in: %d (%p)\n",
+                    copy,
+                    fileno(copy->parent_stdout), copy->parent_stdout,
+                    fileno(copy->parent_stdin), copy->parent_stdin);
   }
+  /*
+   * Duplicated environments are not managed.
+   */
+  copy->managed = false;
 }
 
 /*
@@ -201,14 +305,21 @@ static int rtems_shell_line_editor(
   int          up;
   int          cmd = -1;
   int          inserting = 1;
+  int          in_fileno = fileno(in);
+  int          out_fileno = fileno(out);
 
-  output = (out && isatty(fileno(in)));
+  /*
+   * Only this task can use this file descriptor because calling
+   * fileno will block if another thread call made a call on this
+   * descriptor.
+   */
+  output = (out && isatty(in_fileno));
 
   col = last_col = 0;
 
-  tcdrain(fileno(in));
+  tcdrain(in_fileno);
   if (out)
-    tcdrain(fileno(out));
+    tcdrain(out_fileno);
 
   if (output && prompt)
     fprintf(out, "\r%s", prompt);
@@ -544,9 +655,9 @@ static int rtems_shell_line_editor(
 
 static bool rtems_shell_login(rtems_shell_env_t *env, FILE * in,FILE * out)
 {
-  FILE              *fd;
-  int               c;
-  time_t            t;
+  FILE  *fd;
+  int    c;
+  time_t t;
 
   if (out) {
     if ((env->devname[5]!='p')||
@@ -554,9 +665,9 @@ static bool rtems_shell_login(rtems_shell_env_t *env, FILE * in,FILE * out)
         (env->devname[7]!='y')) {
       fd = fopen("/etc/issue","r");
       if (fd) {
-        while ((c=fgetc(fd))!=EOF) {
+        while ((c = fgetc(fd)) != EOF) {
           if (c=='@')  {
-            switch(c=fgetc(fd)) {
+            switch (c = fgetc(fd)) {
               case 'L':
                 fprintf(out,"%s", env->devname);
                 break;
@@ -688,6 +799,7 @@ static bool rtems_shell_init_user_env(void)
   /* Make sure we have a private user environment */
   sc = rtems_libio_set_private_env();
   if (sc != RTEMS_SUCCESSFUL) {
+    rtems_error(sc, "rtems_libio_set_private_env():");
     return false;
   }
 
@@ -704,103 +816,115 @@ static bool rtems_shell_init_user_env(void)
 #define RTEMS_SHELL_PROMPT_SIZE       (128)
 
 bool rtems_shell_main_loop(
-  rtems_shell_env_t *shell_env_arg
+  rtems_shell_env_t *shell_env
 )
 {
-  rtems_shell_env_t *shell_env;
-  int                eno;
-  struct termios     term;
-  struct termios     previous_term;
-  char              *prompt = NULL;
-  int                cmd;
-  int                cmd_count = 1; /* assume a script and so only 1 command line */
-  char              *cmds[RTEMS_SHELL_CMD_COUNT];
-  char              *cmd_argv;
-  int                argc;
-  char              *argv[RTEMS_SHELL_MAXIMUM_ARGUMENTS];
-  bool               result = true;
-  bool               input_file = false;
-  int                line = 0;
-  FILE              *stdinToClose = NULL;
-  FILE              *stdoutToClose = NULL;
+  struct termios  term;
+  struct termios  previous_term;
+  char           *prompt = NULL;
+  int             cmd;
+  int             cmd_count = 1; /* assume a script and so only 1 command line */
+  char           *cmds[RTEMS_SHELL_CMD_COUNT];
+  char           *cmd_argv;
+  int             argc;
+  char           *argv[RTEMS_SHELL_MAXIMUM_ARGUMENTS];
+  bool            result = true;
+  bool            input_file = false;
+  int             line = 0;
+  FILE           *stdinToClose = NULL;
+  FILE           *stdoutToClose = NULL;
 
   rtems_shell_init_environment();
 
-  shell_env = rtems_shell_init_env(shell_env_arg);
-  if (shell_env == NULL) {
+  if (shell_env->magic != rtems_build_name('S', 'E', 'N', 'V')) {
+    rtems_error(0, "invalid shell environment passed to the main loop)");
     return false;
   }
 
-  eno = pthread_setspecific(rtems_shell_current_env_key, shell_env);
-  if (eno != 0) {
+  if (!rtems_shell_set_shell_env(shell_env))
     return false;
-  }
 
   if (!rtems_shell_init_user_env()) {
+    rtems_error(0, "rtems_shell_init_user_env");
+    rtems_shell_clear_shell_env();
     return false;
   }
 
-  fileno(stdout);
-
-  /* fprintf( stderr,
-     "-%s-%s-\n", shell_env->input, shell_env->output );
-  */
+  shell_std_debug("env: %p\n", shell_env);
 
-  if (shell_env->output && strcmp(shell_env->output, "stdout") != 0) {
-    if (strcmp(shell_env->output, "stderr") == 0) {
+  if (shell_env->output == NULL || strcmp(shell_env->output, "stdout") == 0) {
+    if (shell_env->parent_stdout != NULL)
+      stdout = shell_env->parent_stdout;
+  }
+  else if (strcmp(shell_env->output, "stderr") == 0) {
+    if (shell_env->parent_stderr != NULL)
+      stdout = shell_env->parent_stderr;
+    else
       stdout = stderr;
-    } else if (strcmp(shell_env->output, "/dev/null") == 0) {
-      fclose (stdout);
-    } else {
-      FILE *output = fopen(shell_env_arg->output,
-                           shell_env_arg->output_append ? "a" : "w");
-      if (!output) {
-        fprintf(stderr, "shell: open output %s failed: %s\n",
-                shell_env_arg->output, strerror(errno));
-        return false;
-      }
-      stdout = output;
-      stdoutToClose = output;
+  } else if (strcmp(shell_env->output, "/dev/null") == 0) {
+    fclose (stdout);
+  } else {
+    FILE *output = fopen(shell_env->output,
+                         shell_env->output_append ? "a" : "w");
+    if (output == NULL) {
+      fprintf(stderr, "shell: open output %s failed: %s\n",
+              shell_env->output, strerror(errno));
+      rtems_shell_clear_shell_env();
+      return false;
     }
+    stdout = output;
+    stdoutToClose = output;
   }
 
-  if (shell_env->input && strcmp(shell_env_arg->input, "stdin") != 0) {
-    FILE *input = fopen(shell_env_arg->input, "r");
-    if (!input) {
+  if (shell_env->input == NULL || strcmp(shell_env->input, "stdin") == 0) {
+    if (shell_env->parent_stdin != NULL)
+      stdin = shell_env->parent_stdin;
+  } else {
+    FILE *input = fopen(shell_env->input, "r");
+    if (input == NULL) {
       fprintf(stderr, "shell: open input %s failed: %s\n",
-              shell_env_arg->input, strerror(errno));
+              shell_env->input, strerror(errno));
+      if (stdoutToClose != NULL)
+        fclose(stdoutToClose);
+      rtems_shell_clear_shell_env();
       return false;
     }
     stdin = input;
     stdinToClose = input;
     shell_env->forever = false;
-    input_file =true;
+    input_file = true;
   }
-  else {
-    /* make a raw terminal,Linux Manuals */
+
+  if (!input_file) {
+    /* Make a raw terminal, Linux Manuals */
     if (tcgetattr(fileno(stdin), &previous_term) >= 0) {
       term = previous_term;
       term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
       term.c_oflag &= ~OPOST;
       term.c_oflag |= (OPOST|ONLCR); /* But with cr+nl on output */
       term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
-      term.c_cflag  |= CLOCAL | CREAD;
+      term.c_cflag |= CLOCAL | CREAD;
       term.c_cc[VMIN]  = 1;
       term.c_cc[VTIME] = 0;
       if (tcsetattr (fileno(stdin), TCSADRAIN, &term) < 0) {
         fprintf(stderr,
-                "shell:cannot set terminal attributes(%s)\n",shell_env->devname);
+                "shell: cannot set terminal attributes(%s)\n",shell_env->devname);
       }
     }
     cmd_count = RTEMS_SHELL_CMD_COUNT;
     prompt = malloc(RTEMS_SHELL_PROMPT_SIZE);
     if (!prompt)
         fprintf(stderr,
-                "shell:cannot allocate prompt memory\n");
+                "shell: cannot allocate prompt memory\n");
   }
 
-  setvbuf(stdin,NULL,_IONBF,0); /* Not buffered*/
-  setvbuf(stdout,NULL,_IONBF,0); /* Not buffered*/
+  shell_std_debug("child out: %d (%p)\n", fileno(stdout), stdout);
+  shell_std_debug("child  in: %d (%p)\n", fileno(stdin), stdin);
+
+   /* Do not buffer if interactive else leave buffered */
+  if (!input_file)
+    setvbuf(stdin, NULL, _IONBF, 0);
+  setvbuf(stdout, NULL, _IONBF, 0);
 
   /*
    * Allocate the command line buffers.
@@ -863,8 +987,6 @@ bool rtems_shell_main_loop(
         shell_env->exit_shell = false;
 
         for (;;) {
-          int cmd;
-
           /* Prompt section */
           if (prompt) {
             rtems_shell_get_prompt(shell_env, prompt,
@@ -879,8 +1001,10 @@ bool rtems_shell_main_loop(
           if (cmd == -1)
             continue; /* empty line */
 
-          if (cmd == -2)
+          if (cmd == -2) {
+            result = false;
             break; /*EOF*/
+          }
 
           line++;
 
@@ -918,7 +1042,11 @@ bool rtems_shell_main_loop(
           memcpy (cmd_argv, cmds[cmd], RTEMS_SHELL_CMD_SIZE);
           if (!rtems_shell_make_args(cmd_argv, &argc, argv,
                                      RTEMS_SHELL_MAXIMUM_ARGUMENTS)) {
-            shell_env->errorlevel = rtems_shell_execute_cmd(argv[0], argc, argv);
+            int exit_code = rtems_shell_execute_cmd(argv[0], argc, argv);
+            if (shell_env->exit_code != NULL)
+              *shell_env->exit_code = exit_code;
+            if (exit_code != 0 && shell_env->exit_on_error)
+              shell_env->exit_shell = true;
           }
 
           /* end exec cmd section */
@@ -929,6 +1057,7 @@ bool rtems_shell_main_loop(
         fflush( stdout );
         fflush( stderr );
       }
+      shell_std_debug("end: %d %d\n", result, shell_env->forever);
     } while (result && shell_env->forever);
 
   }
@@ -940,6 +1069,9 @@ bool rtems_shell_main_loop(
   if (prompt)
     free (prompt);
 
+  shell_std_debug("child in-to-close: %p\n", stdinToClose);
+  shell_std_debug("child out-to-close: %p\n", stdoutToClose);
+
   if (stdinToClose) {
     fclose( stdinToClose );
   } else {
@@ -953,6 +1085,7 @@ bool rtems_shell_main_loop(
   }
   if ( stdoutToClose )
     fclose( stdoutToClose );
+  rtems_shell_clear_shell_env();
   return result;
 }
 
@@ -968,6 +1101,7 @@ static rtems_status_code rtems_shell_run (
   const char *output,
   bool output_append,
   rtems_id wake_on_end,
+  int *exit_code,
   bool echo,
   rtems_shell_login_check_t login_check
 )
@@ -977,6 +1111,8 @@ static rtems_status_code rtems_shell_run (
   rtems_shell_env_t *shell_env;
   rtems_name         name;
 
+  rtems_shell_init_environment();
+
   if ( task_name && strlen(task_name) >= 4)
     name = rtems_build_name(
       task_name[0], task_name[1], task_name[2], task_name[3]);
@@ -992,31 +1128,51 @@ static rtems_status_code rtems_shell_run (
     &task_id
   );
   if (sc != RTEMS_SUCCESSFUL) {
+    rtems_error(sc,"creating task %s in shell_init()",task_name);
     return sc;
   }
 
   shell_env = rtems_shell_init_env( NULL );
   if ( !shell_env )  {
+   rtems_error(RTEMS_NO_MEMORY,
+               "allocating shell_env %s in shell_init()",task_name);
    return RTEMS_NO_MEMORY;
   }
+
+  shell_std_debug("run: env: %p\n", shell_env);
+
   shell_env->devname       = devname;
   shell_env->taskname      = task_name;
+
   shell_env->exit_shell    = false;
   shell_env->forever       = forever;
   shell_env->echo          = echo;
-  shell_env->input         = strdup (input);
-  shell_env->output        = strdup (output);
+  shell_env->input         = input == NULL ? NULL : strdup (input);
+  shell_env->output        = output == NULL ? NULL : strdup (output);
   shell_env->output_append = output_append;
+  shell_env->parent_stdin  = stdin;
+  shell_env->parent_stdout = stdout;
+  shell_env->parent_stderr = stderr;
   shell_env->wake_on_end   = wake_on_end;
+  shell_env->exit_code     = exit_code;
   shell_env->login_check   = login_check;
   shell_env->uid           = getuid();
   shell_env->gid           = getgid();
 
   getcwd(shell_env->cwd, sizeof(shell_env->cwd));
 
+  shell_std_debug("run out: %d (%p)\n",
+                  fileno(shell_env->parent_stdout), shell_env->parent_stdout);
+  shell_std_debug("run  in: %d (%p)\n",
+                  fileno(shell_env->parent_stdin), shell_env->parent_stdin);
+
   sc = rtems_task_start(task_id, rtems_shell_task,
-                          (rtems_task_argument) shell_env);
+                        (rtems_task_argument) shell_env);
   if (sc != RTEMS_SUCCESSFUL) {
+    rtems_error(sc,"starting task %s in shell_init()",task_name);
+    free( (void*) shell_env->input );
+    free( (void*) shell_env->output );
+    free( shell_env );
     return sc;
   }
 
@@ -1025,7 +1181,9 @@ static rtems_status_code rtems_shell_run (
     sc = rtems_event_receive (RTEMS_EVENT_1, RTEMS_WAIT, 0, &out);
   }
 
-  return 0;
+  shell_std_debug("run: end: sc:%d ec:%d\n", sc, *exit_code);
+
+  return sc;
 }
 
 rtems_status_code rtems_shell_init(
@@ -1039,6 +1197,7 @@ rtems_status_code rtems_shell_init(
 )
 {
   rtems_id to_wake = RTEMS_ID_NONE;
+  int exit_code = 0;
 
   if ( wait )
     to_wake = rtems_task_self();
@@ -1054,12 +1213,13 @@ rtems_status_code rtems_shell_init(
     "stdout",                /* output */
     false,                   /* output_append */
     to_wake,                 /* wake_on_end */
+    &exit_code,              /* exit code of command */
     false,                   /* echo */
     login_check              /* login check */
   );
 }
 
-rtems_status_code   rtems_shell_script (
+rtems_status_code rtems_shell_script (
   const char          *task_name,
   size_t               task_stacksize,
   rtems_task_priority  task_priority,
@@ -1070,14 +1230,14 @@ rtems_status_code   rtems_shell_script (
   bool                 echo
 )
 {
-  rtems_id          current_task = RTEMS_INVALID_ID;
+  rtems_id to_wake = RTEMS_ID_NONE;
+  int exit_code = 0;
   rtems_status_code sc;
 
-  if (wait) {
-    sc = rtems_task_ident (RTEMS_SELF, RTEMS_LOCAL, &current_task);
-    if (sc != RTEMS_SUCCESSFUL)
-      return sc;
-  }
+  shell_std_debug("script: in: %s out: %s\n", input, output);
+
+  if ( wait )
+    to_wake = rtems_task_self();
 
   sc = rtems_shell_run(
     task_name,       /* task_name */
@@ -1089,12 +1249,19 @@ rtems_status_code   rtems_shell_script (
     input,           /* input */
     output,          /* output */
     output_append,   /* output_append */
-    current_task,    /* wake_on_end */
+    to_wake,         /* wake_on_end */
+    &exit_code,      /* exit_code */
     echo,            /* echo */
     NULL             /* login check */
   );
-  if (sc != RTEMS_SUCCESSFUL)
-    return sc;
+
+  if (sc == RTEMS_SUCCESSFUL)
+  {
+    /* Place holder until RTEMS 5 is released then the interface for
+     * this call will change. */
+  }
+
+  shell_std_debug("script: end: %d\n", sc);
 
   return sc;
 }
diff --git a/testsuites/libtests/shell01/init.c b/testsuites/libtests/shell01/init.c
index 545d695d86..5085c3c803 100644
--- a/testsuites/libtests/shell01/init.c
+++ b/testsuites/libtests/shell01/init.c
@@ -50,6 +50,122 @@ static const char etc_group[] =
   "E::7:y,z\n"
   "F::8:s,moop,t\n";
 
+static const char joel_in[] =
+  "#! joel\n"
+  "jtst hello world\n"
+  "jtst 1 2 3 4 5\n";
+
+static const char joel_out_1[] =
+  " 3 'jtst hello world'\n"
+  " 6 'jtst 1 2 3 4 5'\n";
+
+static const char joel_out_2[] =
+  "\n"
+  "RTEMS Shell on (null). Use 'help' to list commands.\n"
+  " 3 'jtst hello world'\n"
+  " 6 'jtst 1 2 3 4 5'\n";
+
+static int joel_test_command(int argc, char** argv)
+{
+  int i;
+  fprintf(stdout, "%2d '", argc);
+  for (i = 0; i < argc; ++i) {
+    fprintf(stdout, argv[i]);
+    if (i < (argc - 1))
+      fprintf(stdout, " ");
+  }
+  fprintf(stdout, "'\n");
+  return 0;
+}
+
+static void test_joel(void)
+{
+  /*
+   * Running a joel script tests the shell main loop.
+   */
+  char buf[sizeof(joel_out_2) + 1];
+  rtems_shell_cmd_t* cmd;
+  FILE *in;
+  FILE *out;
+  FILE *current_stdout = stdout;
+  FILE *current_stdin = stdin;
+  size_t len;
+  rtems_status_code sc;
+
+  /*
+   * Use a private command due to the way the testsuite maps printk onto printf.
+   */
+  cmd = rtems_shell_add_cmd("jtst", "misc", "joel test", joel_test_command);
+  rtems_test_assert(cmd != NULL);
+
+  len = strlen(joel_in);
+
+  in = fopen("/jin", "w");
+  rtems_test_assert(in != NULL);
+  rtems_test_assert(fwrite(joel_in, sizeof(char), len, in) == len);
+  rtems_test_assert(fclose(in) == 0);
+
+  /*
+   * The shell opens the input and output files.
+   */
+  sc = rtems_shell_script(
+    "JTST",
+    8 * 1024,
+    1,
+    "/jin",
+    "/jout",
+    false,
+    true,
+    false);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  out = fopen("/jout", "r");
+  rtems_test_assert(out != NULL);
+  rtems_test_assert(!feof(out));
+  memset(buf, 0, sizeof(buf));
+  len = fread(buf, sizeof(char), sizeof(buf), out);
+  rtems_test_assert(len > 0);
+  rtems_test_assert(strcmp(joel_out_1, buf) == 0);
+  rtems_test_assert(fclose(out) == 0);
+
+  /*
+   * The shell task inherits the parent stdin and stdout
+   */
+  in = fopen("/jin", "r");
+  rtems_test_assert(in != NULL);
+  out = fopen("/jout", "w");
+  rtems_test_assert(out != NULL);
+
+  stdin = in;
+  stdout = out;
+
+  sc = rtems_shell_script(
+    "JTST",
+    8 * 1024,
+    1,
+    "stdin",
+    "stdout",
+    false,
+    true,
+    false);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  stdout = current_stdout;
+  stdin = current_stdin;
+
+  rtems_test_assert(fclose(in) == 0);
+  rtems_test_assert(fclose(out) == 0);
+
+  out = fopen("/jout", "r");
+  rtems_test_assert(out != NULL);
+  rtems_test_assert(!feof(out));
+  memset(buf, 0, sizeof(buf));
+  len = fread(buf, sizeof(char), sizeof(buf), out);
+  rtems_test_assert(len > 0);
+  rtems_test_assert(strcmp(joel_out_2, buf) == 0);
+  rtems_test_assert(fclose(out) == 0);
+}
+
 static void test(void)
 {
   rtems_user_env_t *uenv;
@@ -163,6 +279,7 @@ static void Init(rtems_task_argument arg)
   TEST_BEGIN();
 
   test();
+  test_joel();
 
   TEST_END();
   rtems_test_exit(0);
@@ -171,10 +288,10 @@ static void Init(rtems_task_argument arg)
 #define CONFIGURE_APPLICATION_DOES_NOT_NEED_CLOCK_DRIVER
 #define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
 
-#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 4
+#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 5
 
-#define CONFIGURE_MAXIMUM_TASKS 1
-#define CONFIGURE_MAXIMUM_POSIX_KEYS 1
+#define CONFIGURE_MAXIMUM_TASKS 3
+#define CONFIGURE_MAXIMUM_POSIX_KEYS 2
 #define CONFIGURE_MAXIMUM_POSIX_KEY_VALUE_PAIRS 2
 
 #define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
-- 
2.24.1



More information about the devel mailing list