Changeset 4d906d6a in rtems


Ignore:
Timestamp:
Feb 18, 2020, 12:34:46 AM (7 weeks ago)
Author:
Chris Johns <chrisj@…>
Branches:
4.11
Parents:
fe09d8d
git-author:
Chris Johns <chrisj@…> (02/18/20 00:34:46)
git-committer:
Chris Johns <chrisj@…> (02/19/20 08:05:54)
Message:

libmisc/shell: Fix the handling of joel scripts in telnet

  • Fix the passing of std[in/out] to child threads
  • Fix deleting of managed memory in the key destructor
  • Only set the key in the main loop thread
  • Only allocate a shell env outside of the main loop
  • Fix memory leak if the task start fails
  • Remove error level from shell env, it cannot be returned this way. Add exit_code but the API is broken so it cannot be returned.

Closes #3877

Files:
3 edited

Legend:

Unmodified
Added
Removed
  • cpukit/libmisc/shell/shell.c

    rfe09d8d r4d906d6a  
    4141#include <assert.h>
    4242
     43#define SHELL_STD_DEBUG 0
     44
     45#if SHELL_STD_DEBUG
     46static FILE* default_stdout;
     47#define shell_std_debug(...) fprintf(default_stdout, __VA_ARGS__)
     48#else
     49#define shell_std_debug(...)
     50#endif
     51
    4352const rtems_shell_env_t rtems_global_shell_env = {
    4453  .magic         = rtems_build_name('S', 'E', 'N', 'V'),
     54  .managed       = false,
    4555  .devname       = CONSOLE_DEVICE_NAME,
    4656  .taskname      = "SHGL",
    4757  .exit_shell    = false,
    4858  .forever       = true,
    49   .errorlevel    = -1,
    5059  .echo          = false,
    5160  .cwd           = "/",
     
    5362  .output        = NULL,
    5463  .output_append = false,
     64  .parent_stdin  = NULL,
     65  .parent_stdout = NULL,
     66  .parent_stderr = NULL,
    5567  .wake_on_end   = RTEMS_ID_NONE,
    56   .login_check   = NULL
     68  .exit_code     = NULL,
     69  .login_check   = NULL,
     70  .uid           = 0,
     71  .gid           = 0
    5772};
     73
     74typedef struct rtems_shell_env_key_handle
     75{
     76  bool managed;
     77  rtems_shell_env_t* env;
     78} rtems_shell_env_key_handle;
    5879
    5980static pthread_once_t rtems_shell_once = PTHREAD_ONCE_INIT;
     
    6586 */
    6687static rtems_shell_env_t *rtems_shell_init_env(
    67   rtems_shell_env_t *shell_env_p
     88  rtems_shell_env_t *shell_env_parent
    6889)
    6990{
     
    7394  if ( !shell_env )
    7495    return NULL;
    75   if ( !shell_env_p ) {
     96
     97  if ( shell_env_parent == NULL ) {
     98    shell_env_parent = rtems_shell_get_current_env();
     99  }
     100  if ( shell_env_parent == NULL ) {
    76101    *shell_env = rtems_global_shell_env;
    77102  } else {
    78     *shell_env = *shell_env_p;
    79   }
     103    *shell_env = *shell_env_parent;
     104  }
     105  shell_env->managed = true;
    80106  shell_env->taskname = NULL;
    81107
     
    90116)
    91117{
    92   rtems_shell_env_t *shell_env;
    93   shell_env = (rtems_shell_env_t *) ptr;
    94 
    95   if ( !ptr )
    96     return;
    97 
    98   if ( shell_env->input )
    99     free((void *)shell_env->input);
    100   if ( shell_env->output )
    101     free((void *)shell_env->output);
    102   free( ptr );
     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( ptr );
     128    }
     129
     130    free( handle );
     131  }
    103132}
    104133
     
    131160  struct passwd pwd;
    132161  struct passwd *pwd_res;
     162
     163#if SHELL_STD_DEBUG
     164  default_stdout = stdout;
     165#endif
    133166
    134167  pthread_key_create(&rtems_shell_current_env_key, rtems_shell_env_free);
     
    157190
    158191/*
     192 * Create a current shell key.
     193 */
     194static bool rtems_shell_set_shell_env(
     195  rtems_shell_env_t* shell_env
     196)
     197{
     198  /*
     199   * The shell environment can be managed or it can be provided by a
     200   * user. We need to create a handle to hold the env pointer.
     201   */
     202  rtems_shell_env_key_handle *handle;
     203  int eno;
     204
     205  handle = malloc(sizeof(rtems_shell_env_key_handle));
     206  if (handle == NULL) {
     207    rtems_error(0, "no memory for shell env key handle)");
     208    return false;
     209  }
     210
     211  handle->managed = shell_env->managed;
     212  handle->env = shell_env;
     213
     214  eno = pthread_setspecific(rtems_shell_current_env_key, handle);
     215  if (eno != 0) {
     216    rtems_error(0, "pthread_setspecific(shell_current_env_key)");
     217    return false;
     218  }
     219
     220  return true;
     221}
     222
     223/*
    159224 *  Return the current shell environment
    160225 */
    161226rtems_shell_env_t *rtems_shell_get_current_env(void)
    162227{
    163   return (rtems_shell_env_t *) pthread_getspecific(rtems_shell_current_env_key);
     228  rtems_shell_env_key_handle *handle;
     229  handle = (rtems_shell_env_key_handle*)
     230    pthread_getspecific(rtems_shell_current_env_key);
     231  return handle->env;
    164232}
    165233
     
    171239{
    172240  rtems_shell_env_t *env = rtems_shell_get_current_env();
    173   if (env) {
     241  if (env != NULL) {
    174242    *copy = *env;
    175243  }
    176244  else {
    177     memset(copy, 0, sizeof(rtems_shell_env_t));
    178     copy->magic    = rtems_build_name('S', 'E', 'N', 'V');
    179     copy->devname  = CONSOLE_DEVICE_NAME;
    180     copy->taskname = "RTSH";
    181   }
     245    *copy = rtems_global_shell_env;
     246    copy->magic         = rtems_build_name('S', 'E', 'N', 'V');
     247    copy->devname       = CONSOLE_DEVICE_NAME;
     248    copy->taskname      = "RTSH";
     249    copy->parent_stdout = stdout;
     250    copy->parent_stdin  = stdin;
     251    copy->parent_stderr = stderr;
     252  }
     253  /*
     254   * Duplicated environments are not managed.
     255   */
     256  copy->managed = false;
    182257}
    183258
     
    547622static bool rtems_shell_login(rtems_shell_env_t *env, FILE * in,FILE * out)
    548623{
    549   FILE              *fd;
    550   int               c;
    551   time_t            t;
     624  FILE  *fd;
     625  int    c;
     626  time_t t;
    552627
    553628  if (out) {
     
    708783
    709784bool rtems_shell_main_loop(
    710   rtems_shell_env_t *shell_env_arg
     785  rtems_shell_env_t *shell_env
    711786)
    712787{
    713   rtems_shell_env_t *shell_env;
    714   int                eno;
    715   struct termios     term;
    716   struct termios     previous_term;
    717   char              *prompt = NULL;
    718   int                cmd;
    719   int                cmd_count = 1; /* assume a script and so only 1 command line */
    720   char              *cmds[RTEMS_SHELL_CMD_COUNT];
    721   char              *cmd_argv;
    722   int                argc;
    723   char              *argv[RTEMS_SHELL_MAXIMUM_ARGUMENTS];
    724   bool               result = true;
    725   bool               input_file = false;
    726   int                line = 0;
    727   FILE              *stdinToClose = NULL;
    728   FILE              *stdoutToClose = NULL;
     788  struct termios  term;
     789  struct termios  previous_term;
     790  char           *prompt = NULL;
     791  int             cmd;
     792  int             cmd_count = 1; /* assume a script and so only 1 command line */
     793  char           *cmds[RTEMS_SHELL_CMD_COUNT];
     794  char           *cmd_argv;
     795  int             argc;
     796  char           *argv[RTEMS_SHELL_MAXIMUM_ARGUMENTS];
     797  bool            result = true;
     798  bool            input_file = false;
     799  int             line = 0;
     800  FILE           *stdinToClose = NULL;
     801  FILE           *stdoutToClose = NULL;
    729802
    730803  rtems_shell_init_environment();
    731804
    732   shell_env = rtems_shell_init_env(shell_env_arg);
    733   if (shell_env == NULL) {
    734     rtems_error(0, "rtems_shell_init_env");
     805  if (shell_env->magic != rtems_build_name('S', 'E', 'N', 'V')) {
     806    rtems_error(0, "invalid shell environment passed to the main loop)");
    735807    return false;
    736808  }
    737809
    738   eno = pthread_setspecific(rtems_shell_current_env_key, shell_env);
    739   if (eno != 0) {
    740     rtems_error(0, "pthread_setspecific(shell_current_env_key)");
     810  if (!rtems_shell_set_shell_env(shell_env))
    741811    return false;
    742   }
    743812
    744813  if (!rtems_shell_init_user_env()) {
     
    747816  }
    748817
    749   fileno(stdout);
    750 
    751   /* fprintf( stderr,
    752      "-%s-%s-\n", shell_env->input, shell_env->output );
    753   */
    754 
    755   if (shell_env->output && strcmp(shell_env->output, "stdout") != 0) {
    756     if (strcmp(shell_env->output, "stderr") == 0) {
    757       stdout = stderr;
     818  shell_std_debug("shell: out: %d %d %s\n",
     819                  fileno(stdout), fileno(shell_env->parent_stdout),
     820                  shell_env->output);
     821  shell_std_debug("     :  in: %d %d %s\n",
     822                  fileno(stdin), fileno(shell_env->parent_stdin),
     823                  shell_env->input);
     824
     825  if (shell_env->output != NULL) {
     826    if (strcmp(shell_env->output, "stdout") == 0) {
     827      if (shell_env->parent_stdout != NULL)
     828        stdout = shell_env->parent_stdout;
     829    }
     830    else if (strcmp(shell_env->output, "stderr") == 0) {
     831      if (shell_env->parent_stderr != NULL)
     832        stdout = shell_env->parent_stderr;
     833      else
     834        stdout = stderr;
    758835    } else if (strcmp(shell_env->output, "/dev/null") == 0) {
    759836      fclose (stdout);
    760837    } else {
    761       FILE *output = fopen(shell_env_arg->output,
    762                            shell_env_arg->output_append ? "a" : "w");
    763       if (!output) {
     838      FILE *output = fopen(shell_env->output,
     839                           shell_env->output_append ? "a" : "w");
     840      if (output == NULL) {
    764841        fprintf(stderr, "shell: open output %s failed: %s\n",
    765                 shell_env_arg->output, strerror(errno));
     842                shell_env->output, strerror(errno));
    766843        return false;
    767844      }
     
    771848  }
    772849
    773   if (shell_env->input && strcmp(shell_env_arg->input, "stdin") != 0) {
    774     FILE *input = fopen(shell_env_arg->input, "r");
    775     if (!input) {
    776       fprintf(stderr, "shell: open input %s failed: %s\n",
    777               shell_env_arg->input, strerror(errno));
    778       return false;
     850  if (shell_env->input != NULL) {
     851    if (strcmp(shell_env->input, "stdin") == 0) {
     852      if (shell_env->parent_stdin != NULL)
     853        stdin = shell_env->parent_stdin;
     854    } else {
     855      FILE *input = fopen(shell_env->input, "r");
     856      if (input == NULL) {
     857        fprintf(stderr, "shell: open input %s failed: %s\n",
     858                shell_env->input, strerror(errno));
     859        if (stdoutToClose != NULL)
     860          fclose(stdoutToClose);
     861        return false;
     862      }
     863      stdin = input;
     864      stdinToClose = input;
     865      shell_env->forever = false;
     866      input_file = true;
    779867    }
    780     stdin = input;
    781     stdinToClose = input;
    782     shell_env->forever = false;
    783     input_file =true;
    784   }
    785   else {
    786     /* make a raw terminal,Linux Manuals */
     868  }
     869
     870  if (!input_file) {
     871    /* Make a raw terminal, Linux Manuals */
    787872    if (tcgetattr(fileno(stdin), &previous_term) >= 0) {
    788873      term = previous_term;
     
    791876      term.c_oflag |= (OPOST|ONLCR); /* But with cr+nl on output */
    792877      term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
    793       term.c_cflag  |= CLOCAL | CREAD;
     878      term.c_cflag |= CLOCAL | CREAD;
    794879      term.c_cc[VMIN]  = 1;
    795880      term.c_cc[VTIME] = 0;
     
    806891  }
    807892
    808   setvbuf(stdin,NULL,_IONBF,0); /* Not buffered*/
    809   setvbuf(stdout,NULL,_IONBF,0); /* Not buffered*/
     893  shell_std_debug("shell: child out: %d in %d\n", fileno(stdout), fileno(stdin));
     894  shell_std_debug("shell: child out: %p\n", stdout);
     895
     896   /* Do not buffer if interactive else leave buffered */
     897  if (!input_file)
     898    setvbuf(stdin, NULL, _IONBF, 0);
     899  setvbuf(stdout, NULL, _IONBF, 0);
    810900
    811901  /*
     
    870960
    871961        for (;;) {
    872           int cmd;
    873 
    874962          /* Prompt section */
    875963          if (prompt) {
     
    9251013          if (!rtems_shell_make_args(cmd_argv, &argc, argv,
    9261014                                     RTEMS_SHELL_MAXIMUM_ARGUMENTS)) {
    927             shell_env->errorlevel = rtems_shell_execute_cmd(argv[0], argc, argv);
     1015            int exit_code = rtems_shell_execute_cmd(argv[0], argc, argv);
     1016            if (shell_env->exit_code != NULL)
     1017              *shell_env->exit_code = exit_code;
     1018            if (exit_code != 0 && shell_env->exit_on_error)
     1019              shell_env->exit_shell = true;
    9281020          }
    9291021
     
    9751067  bool output_append,
    9761068  rtems_id wake_on_end,
     1069  int *exit_code,
    9771070  bool echo,
    9781071  rtems_shell_login_check_t login_check
     
    9831076  rtems_shell_env_t *shell_env;
    9841077  rtems_name         name;
     1078
     1079  rtems_shell_init_environment();
    9851080
    9861081  if ( task_name && strlen(task_name) >= 4)
     
    10091104   return RTEMS_NO_MEMORY;
    10101105  }
     1106
    10111107  shell_env->devname       = devname;
    10121108  shell_env->taskname      = task_name;
     1109
    10131110  shell_env->exit_shell    = false;
    10141111  shell_env->forever       = forever;
    10151112  shell_env->echo          = echo;
    1016   shell_env->input         = strdup (input);
    1017   shell_env->output        = strdup (output);
     1113  shell_env->input         = input == NULL ? NULL : strdup (input);
     1114  shell_env->output        = output == NULL ? NULL : strdup (output);
    10181115  shell_env->output_append = output_append;
     1116  shell_env->parent_stdin  = stdin;
     1117  shell_env->parent_stdout = stdout;
     1118  shell_env->parent_stderr = stderr;
    10191119  shell_env->wake_on_end   = wake_on_end;
     1120  shell_env->exit_code     = exit_code;
    10201121  shell_env->login_check   = login_check;
    10211122  shell_env->uid           = getuid();
     
    10251126
    10261127  sc = rtems_task_start(task_id, rtems_shell_task,
    1027                           (rtems_task_argument) shell_env);
     1128                        (rtems_task_argument) shell_env);
    10281129  if (sc != RTEMS_SUCCESSFUL) {
    10291130    rtems_error(sc,"starting task %s in shell_init()",task_name);
     1131    free( (void*) shell_env->input );
     1132    free( (void*) shell_env->output );
     1133    free( shell_env );
    10301134    return sc;
    10311135  }
     
    10361140  }
    10371141
    1038   return 0;
     1142  return sc;
    10391143}
    10401144
     
    10501154{
    10511155  rtems_id to_wake = RTEMS_ID_NONE;
     1156  int exit_code = 0;
    10521157
    10531158  if ( wait )
     
    10651170    false,                   /* output_append */
    10661171    to_wake,                 /* wake_on_end */
     1172    &exit_code,              /* exit code of command */
    10671173    false,                   /* echo */
    10681174    login_check              /* login check */
     
    10701176}
    10711177
    1072 rtems_status_code   rtems_shell_script (
     1178rtems_status_code rtems_shell_script (
    10731179  const char          *task_name,
    10741180  size_t               task_stacksize,
     
    10811187)
    10821188{
    1083   rtems_id          current_task = RTEMS_INVALID_ID;
     1189  rtems_id to_wake = RTEMS_ID_NONE;
     1190  int exit_code = 0;
    10841191  rtems_status_code sc;
    10851192
    1086   if (wait) {
    1087     sc = rtems_task_ident (RTEMS_SELF, RTEMS_LOCAL, &current_task);
    1088     if (sc != RTEMS_SUCCESSFUL)
    1089       return sc;
    1090   }
    1091 
    1092   sc = rtems_shell_run(
     1193  if ( wait )
     1194    to_wake = rtems_task_self();
     1195
     1196  return rtems_shell_run(
    10931197    task_name,       /* task_name */
    10941198    task_stacksize,  /* task_stacksize */
     
    11001204    output,          /* output */
    11011205    output_append,   /* output_append */
    1102     current_task,    /* wake_on_end */
     1206    to_wake,         /* wake_on_end */
     1207    &exit_code,      /* exit_code */
    11031208    echo,            /* echo */
    11041209    NULL             /* login check */
    11051210  );
    1106   if (sc != RTEMS_SUCCESSFUL)
    1107     return sc;
     1211
     1212  if (sc == RTEMS_SUCCESSFUL)
     1213  {
     1214    /* Place holder until RTEMS 5 is released then the interface for
     1215     * this call will change. */
     1216  }
    11081217
    11091218  return sc;
  • cpukit/libmisc/shell/shell.h

    rfe09d8d r4d906d6a  
    218218 *  Private environment associated with each shell instance.
    219219 */
    220 typedef struct {
     220typedef struct rtems_shell_env {
    221221  /** 'S','E','N','V': Shell Environment */
    222222  rtems_name magic;
     223  bool managed;
    223224  const char *devname;
    224225  const char *taskname;
    225226  bool exit_shell; /* logout */
    226227  bool forever; /* repeat login */
    227   int errorlevel;
     228  int *exit_code;
     229  bool exit_on_error;
    228230  bool echo;
    229231  char cwd[256];
     
    231233  const char *output;
    232234  bool output_append;
     235  FILE *parent_stdin;
     236  FILE *parent_stdout;
     237  FILE *parent_stderr;
    233238  rtems_id wake_on_end;
    234239  rtems_shell_login_check_t login_check;
  • testsuites/libtests/shell01/init.c

    rfe09d8d r4d906d6a  
    4747}
    4848
     49static const char joel_in[] =
     50  "#! joel\n"
     51  "jtst hello world\n"
     52  "jtst 1 2 3 4 5\n";
     53
     54static const char joel_out_1[] =
     55  " 3 'jtst hello world'\n"
     56  " 6 'jtst 1 2 3 4 5'\n";
     57
     58static const char joel_out_2[] =
     59  "\n"
     60  "RTEMS Shell on (null). Use 'help' to list commands.\n"
     61  " 3 'jtst hello world'\n"
     62  " 6 'jtst 1 2 3 4 5'\n";
     63
     64static int joel_test_command(int argc, char** argv)
     65{
     66  int i;
     67  fprintf(stdout, "%2d '", argc);
     68  for (i = 0; i < argc; ++i) {
     69    fprintf(stdout, argv[i]);
     70    if (i < (argc - 1))
     71      fprintf(stdout, " ");
     72  }
     73  fprintf(stdout, "'\n");
     74  return 0;
     75}
     76
     77static void test_joel(void)
     78{
     79  /*
     80   * Running a joel script tests the shell main loop.
     81   */
     82  char buf[sizeof(joel_out_2) + 1];
     83  rtems_shell_cmd_t* cmd;
     84  FILE *in;
     85  FILE *out;
     86  FILE *current_stdout = stdout;
     87  FILE *current_stdin = stdin;
     88  size_t len;
     89  rtems_status_code sc;
     90
     91  /*
     92   * Use a private command due to the way the testsuite maps printk onto printf.
     93   */
     94  cmd = rtems_shell_add_cmd("jtst", "misc", "joel test", joel_test_command);
     95  rtems_test_assert(cmd != NULL);
     96
     97  len = strlen(joel_in);
     98
     99  in = fopen("/jin", "w");
     100  rtems_test_assert(in != NULL);
     101  rtems_test_assert(fwrite(joel_in, sizeof(char), len, in) == len);
     102  rtems_test_assert(fclose(in) == 0);
     103
     104  /*
     105   * The shell opens the input and output files.
     106   */
     107  sc = rtems_shell_script(
     108    "JTST",
     109    8 * 1024,
     110    1,
     111    "/jin",
     112    "/jout",
     113    false,
     114    true,
     115    false);
     116  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
     117
     118  out = fopen("/jout", "r");
     119  rtems_test_assert(out != NULL);
     120  rtems_test_assert(!feof(out));
     121  memset(buf, 0, sizeof(buf));
     122  len = fread(buf, sizeof(char), sizeof(buf), out);
     123  rtems_test_assert(len > 0);
     124  rtems_test_assert(strcmp(joel_out_1, buf) == 0);
     125  rtems_test_assert(fclose(out) == 0);
     126
     127  /*
     128   * The shell task inherits the parent stdin and stdout
     129   */
     130  in = fopen("/jin", "r");
     131  rtems_test_assert(in != NULL);
     132  out = fopen("/jout", "w");
     133  rtems_test_assert(out != NULL);
     134
     135  stdin = in;
     136  stdout = out;
     137
     138  sc = rtems_shell_script(
     139    "JTST",
     140    8 * 1024,
     141    1,
     142    "stdin",
     143    "stdout",
     144    false,
     145    true,
     146    false);
     147  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
     148
     149  stdout = current_stdout;
     150  stdin = current_stdin;
     151
     152  rtems_test_assert(fclose(in) == 0);
     153  rtems_test_assert(fclose(out) == 0);
     154
     155  out = fopen("/jout", "r");
     156  rtems_test_assert(out != NULL);
     157  rtems_test_assert(!feof(out));
     158  memset(buf, 0, sizeof(buf));
     159  len = fread(buf, sizeof(char), sizeof(buf), out);
     160  rtems_test_assert(len > 0);
     161  rtems_test_assert(strcmp(joel_out_2, buf) == 0);
     162  rtems_test_assert(fclose(out) == 0);
     163}
     164
    49165static void test(void)
    50166{
     
    166282
    167283  test();
     284  test_joel();
    168285
    169286  TEST_END();
     
    174291#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
    175292
    176 #define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 4
    177 
    178 #define CONFIGURE_MAXIMUM_TASKS 1
    179 #define CONFIGURE_MAXIMUM_POSIX_KEYS 1
     293#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 5
     294
     295#define CONFIGURE_MAXIMUM_TASKS 3
     296#define CONFIGURE_MAXIMUM_POSIX_KEYS 2
    180297#define CONFIGURE_MAXIMUM_POSIX_KEY_VALUE_PAIRS 2
    181298
Note: See TracChangeset for help on using the changeset viewer.