Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:19

0001 /**
0002  * @file
0003  *
0004  * @brief Instantatiate a new terminal shell.
0005  */
0006 
0007 /*
0008  * Copyright (c) 2001 Fernando Ruiz Casas <fruizcasas@gmail.com>
0009  *
0010  *  The license and distribution terms for this file may be
0011  *  found in the file LICENSE in this distribution or at
0012  *  http://www.rtems.org/license/LICENSE.
0013  */
0014 
0015 #ifdef HAVE_CONFIG_H
0016 #include "config.h"
0017 #endif
0018 
0019 #include <stdio.h>
0020 #include <time.h>
0021 
0022 #include <rtems.h>
0023 #include <rtems/error.h>
0024 #include <rtems/libio.h>
0025 #include <rtems/libio_.h>
0026 #include <rtems/shell.h>
0027 #include <rtems/shellconfig.h>
0028 #include <rtems/console.h>
0029 #include "internal.h"
0030 
0031 #include <termios.h>
0032 #include <string.h>
0033 #include <stdlib.h>
0034 #include <ctype.h>
0035 #include <sys/stat.h>
0036 #include <unistd.h>
0037 #include <errno.h>
0038 #include <pwd.h>
0039 #include <pthread.h>
0040 #include <assert.h>
0041 
0042 #define SHELL_STD_DEBUG 0
0043 
0044 #if SHELL_STD_DEBUG
0045 #include <rtems/bspIo.h>
0046 #define shell_std_debug(...) \
0047   do { printk("shell[%08x]: ", rtems_task_self()); printk(__VA_ARGS__); } while (0)
0048 #else
0049 #define shell_std_debug(...)
0050 #endif
0051 
0052 #define SHELL_MAGIC rtems_build_name('S', 'E', 'N', 'V')
0053 
0054 const rtems_shell_env_t rtems_global_shell_env = {
0055   .magic         = SHELL_MAGIC,
0056   .managed       = false,
0057   .devname       = CONSOLE_DEVICE_NAME,
0058   .taskname      = "RTSH",
0059   .exit_shell    = false,
0060   .forever       = true,
0061   .echo          = false,
0062   .cwd           = "/",
0063   .input         = NULL,
0064   .output        = NULL,
0065   .output_append = false,
0066   .parent_stdin  = NULL,
0067   .parent_stdout = NULL,
0068   .parent_stderr = NULL,
0069   .wake_on_end   = RTEMS_ID_NONE,
0070   .exit_code     = NULL,
0071   .login_check   = NULL,
0072   .uid           = 0,
0073   .gid           = 0
0074 };
0075 
0076 typedef struct rtems_shell_env_key_handle
0077 {
0078   bool managed;
0079   rtems_shell_env_t* env;
0080 } rtems_shell_env_key_handle;
0081 
0082 static pthread_once_t rtems_shell_once = PTHREAD_ONCE_INIT;
0083 
0084 static pthread_key_t rtems_shell_current_env_key;
0085 
0086 /*
0087  *  Initialize the shell user/process environment information
0088  */
0089 static rtems_shell_env_t *rtems_shell_init_env(
0090   rtems_shell_env_t *shell_env_parent
0091 )
0092 {
0093   rtems_shell_env_t *shell_env;
0094 
0095   shell_env = malloc(sizeof(rtems_shell_env_t));
0096   if ( !shell_env )
0097     return NULL;
0098 
0099   if ( shell_env_parent == NULL ) {
0100     shell_env_parent = rtems_shell_get_current_env();
0101   }
0102   if ( shell_env_parent == NULL ) {
0103     *shell_env = rtems_global_shell_env;
0104   } else {
0105     *shell_env = *shell_env_parent;
0106   }
0107   shell_env->managed = true;
0108   shell_env->taskname = NULL;
0109 
0110   return shell_env;
0111 }
0112 
0113 /*
0114  *  Completely free a shell_env_t and all associated memory
0115  */
0116 static void rtems_shell_env_free(
0117   void *ptr
0118 )
0119 {
0120   if ( ptr != NULL ) {
0121     rtems_shell_env_key_handle *handle = (rtems_shell_env_key_handle *) ptr;
0122     rtems_shell_env_t *shell_env = handle->env;
0123 
0124     if ( handle->managed ) {
0125       if ( shell_env->input )
0126         free((void *)shell_env->input);
0127       if ( shell_env->output )
0128         free((void *)shell_env->output);
0129       free( shell_env );
0130     }
0131 
0132     free( handle );
0133   }
0134 }
0135 
0136 static void rtems_shell_create_file(const char *name, const char *content)
0137 {
0138   FILE *fp = fopen(name, "wx");
0139 
0140   if (fp != NULL) {
0141     fputs(content, fp);
0142     fclose(fp);
0143   }
0144 }
0145 
0146 static void rtems_shell_init_commands(void)
0147 {
0148   rtems_shell_cmd_t * const *c;
0149   rtems_shell_alias_t * const *a;
0150 
0151   for ( c = rtems_shell_Initial_commands ; *c  ; c++ ) {
0152     rtems_shell_add_cmd_struct( *c );
0153   }
0154 
0155   for ( a = rtems_shell_Initial_aliases ; *a  ; a++ ) {
0156     rtems_shell_alias_cmd( (*a)->name, (*a)->alias );
0157   }
0158 }
0159 
0160 static void rtems_shell_init_once(void)
0161 {
0162   struct passwd pwd;
0163   struct passwd *pwd_res;
0164 
0165   pthread_key_create(&rtems_shell_current_env_key, rtems_shell_env_free);
0166 
0167   /* dummy call to init /etc dir */
0168   getpwuid_r(0, &pwd, NULL, 0, &pwd_res);
0169 
0170   rtems_shell_create_file("etc/issue",
0171                           "\n"
0172                           "Welcome to @V\\n"
0173                           "Login into @S\\n");
0174 
0175   rtems_shell_create_file("/etc/issue.net",
0176                           "\n"
0177                           "Welcome to %v\n"
0178                           "running on %m\n");
0179 
0180   rtems_shell_init_commands();
0181   rtems_shell_register_monitor_commands();
0182 }
0183 
0184 void rtems_shell_init_environment(void)
0185 {
0186   assert(pthread_once(&rtems_shell_once, rtems_shell_init_once) == 0);
0187 }
0188 
0189 /*
0190  * Set the shell env into the current thread's shell key.
0191  */
0192 static bool rtems_shell_set_shell_env(
0193   rtems_shell_env_t* shell_env
0194 )
0195 {
0196   /*
0197    * The shell environment can be managed or it can be provided by a
0198    * user. We need to create a handle to hold the env pointer.
0199    */
0200   rtems_shell_env_key_handle *handle;
0201   int eno;
0202 
0203   rtems_shell_init_environment();
0204 
0205   handle = malloc(sizeof(rtems_shell_env_key_handle));
0206   if (handle == NULL) {
0207     rtems_error(0, "no memory for shell env key handle)");
0208     return false;
0209   }
0210 
0211   handle->managed = shell_env->managed;
0212   handle->env = shell_env;
0213 
0214   eno = pthread_setspecific(rtems_shell_current_env_key, handle);
0215   if (eno != 0) {
0216     rtems_error(0, "pthread_setspecific(shell_current_env_key): set");
0217     return false;
0218   }
0219 
0220   return true;
0221 }
0222 
0223 /*
0224  * Clear the current thread's shell key.
0225  */
0226 static void rtems_shell_clear_shell_env(void)
0227 {
0228   int eno;
0229 
0230   /*
0231    * Run the destructor manually.
0232    */
0233   rtems_shell_env_free(pthread_getspecific(rtems_shell_current_env_key));
0234 
0235   /*
0236    * Clear the key
0237    */
0238   eno = pthread_setspecific(rtems_shell_current_env_key, NULL);
0239   if (eno != 0)
0240     rtems_error(0, "pthread_setspecific(shell_current_env_key): clear");
0241 }
0242 
0243 /*
0244  * Clear stdin, stdout and stderr file pointers so they will not be closed.
0245  */
0246 static void rtems_shell_clear_shell_std_handles(void)
0247 {
0248   stdin = NULL;
0249   stdout = NULL;
0250   stderr = NULL;
0251 }
0252 
0253 /*
0254  *  Return the current shell environment
0255  */
0256 rtems_shell_env_t *rtems_shell_get_current_env(void)
0257 {
0258   rtems_shell_env_key_handle *handle;
0259   handle = (rtems_shell_env_key_handle*)
0260     pthread_getspecific(rtems_shell_current_env_key);
0261   return handle == NULL ? NULL : handle->env;
0262 }
0263 
0264 /*
0265  *  Duplication the current shell environment and if none is set
0266  *  clear it.
0267  */
0268 void rtems_shell_dup_current_env(rtems_shell_env_t *copy)
0269 {
0270   rtems_shell_env_t *env = rtems_shell_get_current_env();
0271   if (env != NULL) {
0272     shell_std_debug("dup: existing parent\n");
0273     *copy = *env;
0274 
0275     /*
0276      * Duplicated environments are not managed.
0277      */
0278     copy->managed = false;
0279   } else {
0280     *copy = rtems_global_shell_env;
0281     copy->parent_stdout = stdout;
0282     copy->parent_stdin  = stdin;
0283     copy->parent_stderr = stderr;
0284     shell_std_debug("dup: global: copy: %p out: %d (%p) in: %d (%p)\n",
0285                     copy,
0286                     fileno(copy->parent_stdout), copy->parent_stdout,
0287                     fileno(copy->parent_stdin), copy->parent_stdin);
0288   }
0289 }
0290 
0291 /*
0292  *  Move a string in a buffer to the left (e.g. when a character
0293  *  is deleted). The string must be NUL-terminated and the
0294  *  NUL-character will be moved too.
0295  */
0296 static void rtems_shell_move_left(char *start, size_t offset)
0297 {
0298   memmove(start, start + offset, strlen(start + offset) + 1);
0299 }
0300 
0301 /*
0302  *  Get a line of user input with modest features
0303  */
0304 static int rtems_shell_line_editor(
0305   char       *cmds[],
0306   int         count,
0307   int         size,
0308   const char *prompt,
0309   FILE       *in,
0310   FILE       *out
0311 )
0312 {
0313   unsigned int extended_key;
0314   int          c;
0315   int          col;
0316   int          last_col;
0317   char         line[size];
0318   char         new_line[size];
0319   int          up;
0320   int          cmd = -1;
0321   int          inserting = 1;
0322   int          in_fileno = fileno(in);
0323 
0324   col = last_col = 0;
0325 
0326   tcdrain(in_fileno);
0327   if (out != NULL) {
0328     tcdrain(fileno(out));
0329   }
0330 
0331   if (out != NULL && prompt != NULL)
0332     fprintf(out, "%s", prompt);
0333 
0334   line[0] = 0;
0335   new_line[0] = 0;
0336 
0337   for (;;) {
0338 
0339     if (out != NULL)
0340       fflush(out);
0341 
0342     extended_key = rtems_shell_getchar(in);
0343 
0344     if (extended_key == EOF)
0345       return -2;
0346 
0347     c = extended_key & RTEMS_SHELL_KEYS_NORMAL_MASK;
0348 
0349     /*
0350      * Make the extended_key usable as a boolean.
0351      */
0352     extended_key &= ~RTEMS_SHELL_KEYS_NORMAL_MASK;
0353 
0354     up = 0;
0355 
0356     if (extended_key)
0357     {
0358       switch (c)
0359       {
0360         case RTEMS_SHELL_KEYS_END:
0361           if (out != NULL)
0362             fprintf(out, "%s", line + col);
0363           col = (int) strlen (line);
0364           break;
0365 
0366         case RTEMS_SHELL_KEYS_HOME:
0367           if (out != NULL && prompt != NULL) {
0368             fprintf(out,"\r%s", prompt);
0369           }
0370           col = 0;
0371           break;
0372 
0373         case RTEMS_SHELL_KEYS_LARROW:
0374           c = 2;
0375           extended_key = 0;
0376           break;
0377 
0378         case RTEMS_SHELL_KEYS_RARROW:
0379           c = 6;
0380           extended_key = 0;
0381           break;
0382 
0383         case RTEMS_SHELL_KEYS_UARROW:
0384           c = 16;
0385           extended_key = 0;
0386           break;
0387 
0388         case RTEMS_SHELL_KEYS_DARROW:
0389           c = 14;
0390           extended_key = 0;
0391           break;
0392 
0393         case RTEMS_SHELL_KEYS_DEL:
0394           if (line[col] != '\0')
0395           {
0396             int end;
0397             int bs;
0398             rtems_shell_move_left(line + col, 1);
0399             if (out != NULL) {
0400               fprintf(out,"\r%s%s ", prompt, line);
0401               end = (int) strlen (line);
0402               for (bs = 0; bs < ((end - col) + 1); bs++)
0403                 fputc('\b', out);
0404             }
0405           }
0406           break;
0407 
0408         case RTEMS_SHELL_KEYS_INS:
0409           inserting = inserting ? 0 : 1;
0410           break;
0411       }
0412     }
0413     if (!extended_key)
0414     {
0415       switch (c)
0416       {
0417         case 1:                         /*Control-a*/
0418           if (out != NULL && prompt != NULL) {
0419             fprintf(out,"\r%s", prompt);
0420           }
0421           col = 0;
0422           break;
0423 
0424         case 2:                         /* Control-B */
0425           if (col > 0)
0426           {
0427             col--;
0428             if (out != NULL)
0429               fputc('\b', out);
0430           }
0431           break;
0432 
0433         case 4:                         /* Control-D */
0434           if (strlen(line)) {
0435             if (col < strlen(line)) {
0436               rtems_shell_move_left(line + col, 1);
0437               if (out != NULL) {
0438                 int bs;
0439                 fprintf(out,"%s \b", line + col);
0440                 for (bs = 0; bs < ((int) strlen (line) - col); bs++)
0441                   fputc('\b', out);
0442               }
0443             }
0444             break;
0445           }
0446           /* Fall through */
0447 
0448         case EOF:
0449           if (out != NULL)
0450             fputc('\n', out);
0451           return -2;
0452 
0453         case 5:                         /*Control-e*/
0454           if (out != NULL)
0455             fprintf(out, "%s", line + col);
0456           col = (int) strlen (line);
0457           break;
0458 
0459         case 6:                         /* Control-F */
0460           if ((col < size) && (line[col] != '\0')) {
0461             if (out != NULL)
0462               fputc(line[col], out);
0463             col++;
0464           }
0465           break;
0466 
0467         case 7:                         /* Control-G */
0468           if (out != NULL) {
0469             /*
0470              * The (int) cast is needed because the width specifier (%*)
0471              * must be an int, but strlen() returns a size_t. Without
0472              * the case, the result is a printf() format warning.
0473              */
0474             fprintf(out,"\r%s%*c", prompt, (int) strlen (line), ' ');
0475             fprintf(out,"\r%s\x7", prompt);
0476           }
0477           memset (line, '\0', strlen(line));
0478           col = 0;
0479           break;
0480 
0481         case 11:                        /*Control-k*/
0482           if (line[col]) {
0483             if (out != NULL) {
0484               int end = strlen(line);
0485               int bs;
0486               fprintf(out,"%*c", end - col, ' ');
0487               for (bs = 0; bs < (end - col); bs++)
0488                 fputc('\b', out);
0489             }
0490             line[col] = '\0';
0491           }
0492           break;
0493 
0494         case '\f':
0495           if (out != NULL) {
0496             int end;
0497             int bs;
0498             fputc('\f',out);
0499             fprintf(out,"\r%s%s", prompt, line);
0500             end = (int) strlen (line);
0501             for (bs = 0; bs < (end - col); bs++)
0502               fputc('\b', out);
0503           }
0504           break;
0505 
0506         case '\b':
0507         case '\x7f':
0508           if (col > 0)
0509           {
0510             int bs;
0511             col--;
0512             rtems_shell_move_left(line + col, 1);
0513             if (out != NULL) {
0514               fprintf(out,"\b%s \b", line + col);
0515               for (bs = 0; bs < ((int) strlen (line) - col); bs++)
0516                 fputc('\b', out);
0517             }
0518           }
0519           break;
0520 
0521         case '\n':
0522         case '\r':
0523         {
0524           /*
0525            * Process the command.
0526            */
0527           if (out != NULL)
0528             fprintf(out,"\n");
0529 
0530           /*
0531            * Only process the command if we have a command and it is not
0532            * repeated in the history.
0533            */
0534           if (strlen(line) == 0) {
0535             cmd = -1;
0536           } else {
0537             if ((cmd < 0) || (strcmp(line, cmds[cmd]) != 0)) {
0538               if (count > 1)
0539                 memmove(cmds[1], cmds[0], (count - 1) * size);
0540               memmove (cmds[0], line, size);
0541               cmd = 0;
0542             } else {
0543               if ((cmd > 1) && (strcmp(line, cmds[cmd]) == 0)) {
0544                 memmove(cmds[1], cmds[0], cmd * size);
0545                 memmove (cmds[0], line, size);
0546                 cmd = 0;
0547               }
0548             }
0549           }
0550         }
0551         return cmd;
0552 
0553         case 16:                         /* Control-P */
0554           if ((cmd >= (count - 1)) || (strlen(cmds[cmd + 1]) == 0)) {
0555             if (out != NULL)
0556               fputc('\x7', out);
0557             break;
0558           }
0559 
0560           up = 1;
0561           /* drop through */
0562 
0563         case 14:                        /* Control-N */
0564         {
0565           int last_cmd = cmd;
0566           int clen = strlen (line);
0567 
0568           if (prompt)
0569             clen += strlen(prompt);
0570 
0571           if (up) {
0572             cmd++;
0573           } else {
0574             if (cmd < 0) {
0575               if (out != NULL)
0576                 fprintf(out, "\x7");
0577               break;
0578             }
0579             else
0580               cmd--;
0581           }
0582 
0583           if ((last_cmd < 0) || (strcmp(cmds[last_cmd], line) != 0))
0584             memcpy (new_line, line, size);
0585 
0586           if (cmd < 0)
0587             memcpy (line, new_line, size);
0588           else
0589             memcpy (line, cmds[cmd], size);
0590 
0591           col = strlen (line);
0592 
0593           if (out != NULL) {
0594             fprintf(out,"\r%s%*c", prompt, clen, ' ');
0595             fprintf(out,"\r%s%s", prompt, line);
0596           }
0597         }
0598         break;
0599 
0600         case 20:                        /* Control-T */
0601           if (col > 0)
0602           {
0603             char tmp;
0604             if (col == strlen(line)) {
0605               col--;
0606               if (out != NULL)
0607                 fprintf(out,"\b");
0608             }
0609             tmp           = line[col];
0610             line[col]     = line[col - 1];
0611             line[col - 1] = tmp;
0612             if (out != NULL)
0613               fprintf(out,"\b%c%c", line[col - 1], line[col]);
0614             col++;
0615           } else {
0616             if (out != NULL)
0617               fputc('\x7', out);
0618           }
0619           break;
0620 
0621         case 21:                        /* Control-U */
0622           if (col > 0)
0623           {
0624             /* strlen() returns size_t but fprintf("%*...") below requires
0625              * int! */
0626             int clen = (int) strlen (line);
0627             int bs;
0628 
0629             rtems_shell_move_left(line, col);
0630             if (out != NULL) {
0631               fprintf(out,"\r%s%*c", prompt, clen, ' ');
0632               fprintf(out,"\r%s%s", prompt, line);
0633 
0634               for (bs = 0; bs < strlen(line); bs++) {
0635                 fputc('\b', out);
0636               }
0637             }
0638             col = 0;
0639           }
0640           break;
0641 
0642         default:
0643           if ((col < (size - 1)) && (c >= ' ') && (c <= '~')) {
0644             int end = strlen (line);
0645             if (inserting && (col < end) && (end < size)) {
0646               int ch, bs;
0647               for (ch = end + 1; ch > col; ch--)
0648                 line[ch] = line[ch - 1];
0649               if (out != NULL) {
0650                 fprintf(out, "%s", line + col);
0651                 for (bs = 0; bs < (end - col + 1); bs++)
0652                   fputc('\b', out);
0653               }
0654             }
0655             line[col++] = c;
0656             if (col > end)
0657               line[col] = '\0';
0658             if (out != NULL)
0659               fputc(c, out);
0660           }
0661           break;
0662       }
0663     }
0664   }
0665   return -2;
0666 }
0667 
0668 static bool rtems_shell_login(rtems_shell_env_t *env, FILE * in,FILE * out)
0669 {
0670   FILE  *fd;
0671   int    c;
0672   time_t t;
0673 
0674   if (out) {
0675     if ((env->devname[5]!='p')||
0676         (env->devname[6]!='t')||
0677         (env->devname[7]!='y')) {
0678       fd = fopen("/etc/issue","r");
0679       if (fd) {
0680         while ((c = fgetc(fd)) != EOF) {
0681           if (c=='@')  {
0682             switch (c = fgetc(fd)) {
0683               case 'L':
0684                 fprintf(out,"%s", env->devname);
0685                 break;
0686               case 'B':
0687                 fprintf(out,"0");
0688                 break;
0689               case 'T':
0690               case 'D':
0691                 time(&t);
0692                 fprintf(out,"%s",ctime(&t));
0693                 break;
0694               case 'S':
0695                 fprintf(out,"RTEMS");
0696                 break;
0697               case 'V':
0698                 fprintf(
0699                   out,
0700                   "%s\n%s",
0701                   rtems_get_version_string(),
0702                   rtems_get_copyright_notice()
0703                 );
0704                 break;
0705               case '@':
0706                 fprintf(out,"@");
0707                 break;
0708               default :
0709                 fprintf(out,"@%c",c);
0710                 break;
0711             }
0712           } else if (c=='\\')  {
0713             switch(c=fgetc(fd)) {
0714               case '\\': fprintf(out,"\\"); break;
0715               case 'b':  fprintf(out,"\b"); break;
0716               case 'f':  fprintf(out,"\f"); break;
0717               case 'n':  fprintf(out,"\n"); break;
0718               case 'r':  fprintf(out,"\r"); break;
0719               case 's':  fprintf(out," ");  break;
0720               case 't':  fprintf(out,"\t"); break;
0721               case '@':  fprintf(out,"@");  break;
0722             }
0723           } else {
0724             fputc(c,out);
0725           }
0726         }
0727         fclose(fd);
0728       }
0729     } else {
0730       fd = fopen("/etc/issue.net","r");
0731       if (fd) {
0732         while ((c=fgetc(fd))!=EOF) {
0733           if (c=='%')  {
0734             switch(c=fgetc(fd)) {
0735               case 't':
0736                 fprintf(out,"%s", env->devname);
0737                 break;
0738               case 'h':
0739                 fprintf(out,"0");
0740                 break;
0741               case 'D':
0742                 fprintf(out," ");
0743                 break;
0744               case 'd':
0745                 time(&t);
0746                 fprintf(out,"%s",ctime(&t));
0747                 break;
0748               case 's':
0749                 fprintf(out,"RTEMS");
0750                 break;
0751               case 'm':
0752                 fprintf(out,"(" CPU_NAME "/" CPU_MODEL_NAME ")");
0753                 break;
0754               case 'r':
0755                 fprintf(out,rtems_get_version_string());
0756                 break;
0757               case 'v':
0758                 fprintf(
0759                   out,
0760                   "%s\n%s",
0761                   rtems_get_version_string(),
0762                   rtems_get_copyright_notice()
0763                 );
0764             break;
0765           case '%':fprintf(out,"%%");
0766             break;
0767           default:
0768                 fprintf(out,"%%%c",c);
0769                 break;
0770             }
0771           } else {
0772             fputc(c,out);
0773           }
0774         }
0775         fclose(fd);
0776       }
0777     }
0778   }
0779 
0780   return rtems_shell_login_prompt(in, out, env->devname, env->login_check);
0781 }
0782 
0783 #if defined(SHELL_DEBUG)
0784 void rtems_shell_print_env(
0785   rtems_shell_env_t * shell_env
0786 )
0787 {
0788   if ( !shell_env ) {
0789     printk( "shell_env is NULL\n" );
0790 
0791     return;
0792   }
0793   printk( "shell_env=%p\n"
0794     "shell_env->magic=0x%08x\t"
0795     "shell_env->devname=%s\n"
0796     "shell_env->taskname=%s\t"
0797     "shell_env->exit_shell=%d\t"
0798     "shell_env->forever=%d\n",
0799     shell_env->magic,
0800     shell_env->devname,
0801     ((shell_env->taskname) ? shell_env->taskname : "NOT SET"),
0802     shell_env->exit_shell,
0803     shell_env->forever
0804   );
0805 }
0806 #endif
0807 
0808 static int get_ticks_from_ms(const int timeout_ms)
0809 {
0810   int ticks = timeout_ms * 1000;
0811 
0812   ticks /= rtems_configuration_get_microseconds_per_tick();
0813 
0814   return MAX(1, ticks);
0815 }
0816 
0817 /*
0818  * Wait for the string to return or timeout.
0819  */
0820 static bool rtems_shell_term_wait_for(const int fd,
0821                                       const char* str,
0822                                       const int timeout_ms)
0823 {
0824   const int timeout_ticks = get_ticks_from_ms(timeout_ms);
0825   int tick_count = timeout_ticks;
0826   int i = 0;
0827   while (tick_count-- > 0 && str[i] != '\0') {
0828     char ch[2];
0829     if (read(fd, &ch[0], 1) == 1) {
0830       fflush(stdout);
0831       if (ch[0] != str[i++]) {
0832         return false;
0833       }
0834       tick_count = timeout_ticks;
0835     } else {
0836       rtems_task_wake_after(1);
0837     }
0838   }
0839   if (tick_count == 0) {
0840     return false;
0841   }
0842   return true;
0843 }
0844 
0845 /*
0846  * Buffer a string up to the end string
0847  */
0848 static int rtems_shell_term_buffer_until(const int fd,
0849                                          char* buf,
0850                                          const int size,
0851                                          const char* end,
0852                                          const int timeout_ms)
0853 {
0854   const int timeout_ticks = get_ticks_from_ms(timeout_ms);
0855   int tick_count = timeout_ticks;
0856   int i = 0;
0857   int e = 0;
0858   memset(&buf[0], 0, size);
0859   while (tick_count-- > 0 && i < size && end[e] != '\0') {
0860     char ch[2];
0861     if (read(fd, &ch[0], 1) == 1) {
0862       fflush(stdout);
0863       buf[i++] = ch[0];
0864       if (ch[0] == end[e]) {
0865         e++;
0866       } else {
0867         e = 0;
0868       }
0869       tick_count = timeout_ticks;
0870     } else {
0871       rtems_task_wake_after(1);
0872     }
0873   }
0874   if (tick_count == 0 || end[e] != '\0') {
0875     return -1;
0876   }
0877   i -= e;
0878   if (i < size) {
0879     buf[i] = '\0';
0880   }
0881   return i;
0882 }
0883 
0884 /*
0885  * Determine if the terminal has the row and column values
0886  * swapped
0887  *
0888  * https://github.com/tmux/tmux/issues/3457
0889  *
0890  * Tmux has a bug where the lines and cols are swapped. There is a lag
0891  * in the time it takes to get the fix into code so see if tmux is
0892  * running and which version and work around the bug.
0893  *
0894  * The terminal device needs to have VMIN=0, and VTIME=0
0895  */
0896 static bool rtems_shell_term_row_column_swapped(const int fd, const int timeout) {
0897   char buf[64];
0898   memset(&buf[0], 0, sizeof(buf));
0899   /*
0900    * CSI > Ps q
0901    *    Ps = 0   =>   DCS > | text ST
0902    */
0903   fputs("\033[>0q", stdout);
0904   fflush(stdout);
0905   if (rtems_shell_term_wait_for(fd, "\033P>|", timeout)) {
0906     int len = rtems_shell_term_buffer_until(fd, buf, sizeof(buf), "\033\\", timeout);
0907     if (len > 0) {
0908       if (memcmp(buf, "tmux ", 5) == 0) {
0909         static const char* bad_versions[] = {
0910           "3.2", "3.2a", "3.3", "3.3a"
0911         };
0912         size_t i;
0913         for (i = 0; i < RTEMS_ARRAY_SIZE(bad_versions); ++i) {
0914           if (strcmp(bad_versions[i], buf + 5) == 0) {
0915             return true;
0916           }
0917         }
0918       }
0919     }
0920   }
0921   return false;
0922 }
0923 
0924 /*
0925  * Direct method to get the size of an XTERM window.
0926  *
0927  * If you do not use an XTERM the env variables are not define.
0928  */
0929 static void rtems_shell_winsize( void )
0930 {
0931   const int fd = fileno(stdin);
0932   struct winsize ws;
0933   const int timeout = 150;
0934   char buf[64];
0935   bool ok = false;
0936   int lines = 0;
0937   int cols = 0;
0938   int r;
0939   const char *detect = getenv("TERM_SIZE_DETECT");
0940 
0941   if (detect) {
0942     /* Skip window size detection if set to False, false, or 0 */
0943     if (strcmp(detect, "false") == 0) {
0944       return;
0945     }
0946     if (strcmp(detect, "False") == 0) {
0947       return;
0948     }
0949     if (strcmp(detect, "0") == 0) {
0950       return;
0951     }
0952   }
0953 
0954   r = ioctl(fd, TIOCGWINSZ, &ws);
0955   if (r == 0) {
0956     ok = true;
0957     lines = ws.ws_row;
0958     cols = ws.ws_col;
0959   } else if (isatty(fd)) {
0960     struct termios cterm;
0961     if (tcgetattr(fd, &cterm) == 0) {
0962       struct termios term = cterm;
0963       term.c_cc[VMIN] = 0;
0964       term.c_cc[VTIME] = 0;
0965       if (tcsetattr (fd, TCSADRAIN, &term) == 0) {
0966         memset(&buf[0], 0, sizeof(buf));
0967         /*
0968          * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Miscellaneous
0969          *
0970          * CSI 1 8 t
0971          */
0972         fputs("\033[18t", stdout);
0973         fflush(stdout);
0974         if (rtems_shell_term_wait_for(fd, "\033[8;", timeout)) {
0975           int len = rtems_shell_term_buffer_until(fd, buf, sizeof(buf), ";", timeout);
0976           if (len > 0) {
0977             int i;
0978             lines = 0;
0979             i = 0;
0980             while (i < len) {
0981               lines *= 10;
0982               lines += buf[i++] - '0';
0983             }
0984             len = rtems_shell_term_buffer_until(fd, buf, sizeof(buf), "t", timeout);
0985             if (len > 0) {
0986               cols = 0;
0987               i = 0;
0988               while (i < len) {
0989                 cols *= 10;
0990                 cols += buf[i++] - '0';
0991               }
0992               ok = true;
0993             }
0994           }
0995         }
0996       }
0997       if (rtems_shell_term_row_column_swapped(fd, timeout)) {
0998         int tmp = lines;
0999         lines = cols;
1000         cols = tmp;
1001       }
1002       tcsetattr (fd, TCSADRAIN, &cterm);
1003     }
1004   }
1005   if (ok) {
1006     snprintf(buf, sizeof(buf) - 1, "%d", lines);
1007     setenv("LINES", buf, 1);
1008     snprintf(buf, sizeof(buf) - 1, "%d", cols);
1009     setenv("COLUMNS", buf, 1);
1010   } else {
1011     setenv("TERM_SIZE_DETECT", "0", 1);
1012   }
1013 }
1014 
1015 static rtems_task rtems_shell_task(rtems_task_argument task_argument)
1016 {
1017   rtems_shell_env_t *shell_env = (rtems_shell_env_t*) task_argument;
1018   rtems_id           wake_on_end = shell_env->wake_on_end;
1019   rtems_shell_main_loop( shell_env );
1020   rtems_shell_clear_shell_std_handles();
1021   if (wake_on_end != RTEMS_INVALID_ID)
1022     rtems_event_send (wake_on_end, RTEMS_EVENT_1);
1023   rtems_task_exit();
1024 }
1025 
1026 static bool rtems_shell_init_user_env(void)
1027 {
1028   rtems_status_code sc;
1029 
1030   /* Make sure we have a private user environment */
1031   sc = rtems_libio_set_private_env();
1032   if (sc != RTEMS_SUCCESSFUL) {
1033     rtems_error(sc, "rtems_libio_set_private_env():");
1034     return false;
1035   }
1036 
1037   /* Make an effective root user */
1038   seteuid(0);
1039   setegid(0);
1040 
1041   return chroot("/") == 0;
1042 }
1043 
1044 #define RTEMS_SHELL_MAXIMUM_ARGUMENTS (128)
1045 #define RTEMS_SHELL_CMD_SIZE          (128)
1046 #define RTEMS_SHELL_CMD_COUNT         (32)
1047 #define RTEMS_SHELL_PROMPT_SIZE       (128)
1048 
1049 static bool shell_main_loop(
1050   rtems_shell_env_t *shell_env,
1051   bool               interactive,
1052   FILE              *line_editor_output
1053 )
1054 {
1055   bool result = false;
1056   int line = 0;
1057   int cmd_count;
1058   char *cmds[RTEMS_SHELL_CMD_COUNT];
1059   char *cmd_argv;
1060   char *prompt;
1061 
1062   if (interactive) {
1063     prompt = malloc(RTEMS_SHELL_PROMPT_SIZE);
1064     if (prompt == NULL) {
1065       fprintf(stderr, "shell: cannot allocate prompt memory\n");
1066       return false;
1067     }
1068 
1069     cmd_count = RTEMS_SHELL_CMD_COUNT;
1070   } else {
1071     prompt = NULL;
1072     cmd_count = 1;
1073   }
1074 
1075   /*
1076    * Allocate the command line buffers.
1077    */
1078   cmd_argv = malloc (RTEMS_SHELL_CMD_SIZE);
1079   if (!cmd_argv) {
1080     fprintf(stderr, "no memory for command line buffers\n" );
1081   }
1082 
1083   cmds[0] = calloc (cmd_count, RTEMS_SHELL_CMD_SIZE);
1084   if (!cmds[0]) {
1085     fprintf(stderr, "no memory for command line buffers\n" );
1086   }
1087 
1088   if (cmd_argv && cmds[0]) {
1089     size_t cmd;
1090 
1091     memset (cmds[0], 0, cmd_count * RTEMS_SHELL_CMD_SIZE);
1092 
1093     for (cmd = 1; cmd < cmd_count; cmd++) {
1094       cmds[cmd] = cmds[cmd - 1] + RTEMS_SHELL_CMD_SIZE;
1095     }
1096 
1097     do {
1098       result = rtems_shell_init_user_env();
1099 
1100       if (result) {
1101         /*
1102          *  By using result here, we can fall to the bottom of the
1103          *  loop when the connection is dropped during login and
1104          *  keep on trucking.
1105          */
1106         if (shell_env->login_check != NULL) {
1107           result = rtems_shell_login(shell_env, stdin, stdout);
1108         } else {
1109           setuid(shell_env->uid);
1110           setgid(shell_env->gid);
1111           seteuid(shell_env->uid);
1112           setegid(shell_env->gid);
1113           rtems_current_user_env_getgroups();
1114 
1115           result = true;
1116         }
1117       }
1118 
1119       if (result)  {
1120         memset (cmds[0], 0, cmd_count * RTEMS_SHELL_CMD_SIZE);
1121 
1122         if (interactive) {
1123           rtems_shell_cat_file(stdout,"/etc/motd");
1124           fprintf(stdout, "\n"
1125                   "RTEMS Shell on %s. Use 'help' to list commands.\n",
1126                   shell_env->devname);
1127           chdir("/"); /* XXX: chdir to getpwent homedir */
1128         } else {
1129           chdir(shell_env->cwd);
1130         }
1131 
1132         shell_env->exit_shell = false;
1133 
1134         for (;;) {
1135           const char *c;
1136           int argc;
1137           char *argv[RTEMS_SHELL_MAXIMUM_ARGUMENTS];
1138 
1139           /* Prompt section */
1140           if (prompt) {
1141             rtems_shell_get_prompt(shell_env, prompt,
1142                                    RTEMS_SHELL_PROMPT_SIZE);
1143           }
1144 
1145           /* getcmd section */
1146           cmd = rtems_shell_line_editor(cmds, cmd_count,
1147                                         RTEMS_SHELL_CMD_SIZE, prompt,
1148                                         stdin, line_editor_output);
1149 
1150           if (cmd == -1)
1151             continue; /* empty line */
1152 
1153           if (cmd == -2) {
1154             result = false;
1155             break; /*EOF*/
1156           }
1157 
1158           line++;
1159 
1160           if (shell_env->echo)
1161             fprintf(stdout, "%d: %s\n", line, cmds[cmd]);
1162 
1163           /* evaluate cmd section */
1164           c = cmds[cmd];
1165           while (*c) {
1166             if (!isblank((unsigned char)*c))
1167               break;
1168             c++;
1169           }
1170 
1171           if (*c == '\0')   /* empty line */
1172             continue;
1173 
1174           if (*c == '#') {  /* comment character */
1175             cmds[cmd][0] = 0;
1176             continue;
1177           }
1178 
1179           if (!strcmp(cmds[cmd],"bye") || !strcmp(cmds[cmd],"exit")) {
1180             fprintf(stdout, "Shell exiting\n" );
1181             break;
1182           }
1183 
1184           /* exec cmd section */
1185           /* TODO:
1186            *  To avoid user crash catch the signals.
1187            *  Open a new stdio files with posibility of redirection *
1188            *  Run in a new shell task background. (unix &)
1189            *  Resuming. A little bash.
1190            */
1191           memcpy (cmd_argv, cmds[cmd], RTEMS_SHELL_CMD_SIZE);
1192           if (!rtems_shell_make_args(cmd_argv, &argc, argv,
1193                                      RTEMS_SHELL_MAXIMUM_ARGUMENTS)) {
1194             int exit_code;
1195             rtems_shell_winsize();
1196             exit_code = rtems_shell_execute_cmd(argv[0], argc, argv);
1197             if (shell_env->exit_code != NULL)
1198               *shell_env->exit_code = exit_code;
1199             if (exit_code != 0 && shell_env->exit_on_error)
1200               shell_env->exit_shell = true;
1201           }
1202 
1203           /* end exec cmd section */
1204           if (shell_env->exit_shell)
1205             break;
1206         }
1207 
1208         fflush( stdout );
1209         fflush( stderr );
1210       }
1211       shell_std_debug("end: %d %d\n", result, shell_env->forever);
1212     } while (result && shell_env->forever);
1213   }
1214 
1215   free(cmds[0]);
1216   free(cmd_argv);
1217   free(prompt);
1218 
1219   return result;
1220 }
1221 
1222 bool rtems_shell_run_main_loop(
1223   rtems_shell_env_t *shell_env,
1224   bool               interactive,
1225   FILE              *line_editor_output
1226 )
1227 {
1228   bool result;
1229 
1230   if (shell_env->magic != SHELL_MAGIC) {
1231     return false;
1232   }
1233 
1234   if (!rtems_shell_init_user_env()) {
1235     return false;
1236   }
1237 
1238   if (!rtems_shell_set_shell_env(shell_env)) {
1239     return false;
1240   }
1241 
1242   result = shell_main_loop(shell_env, interactive, line_editor_output);
1243   rtems_shell_clear_shell_env();
1244   return result;
1245 }
1246 
1247 bool rtems_shell_main_loop(
1248   rtems_shell_env_t *shell_env
1249 )
1250 {
1251   struct termios  term;
1252   struct termios  previous_term;
1253   bool            result;
1254   bool            interactive = true;
1255   bool            have_previous_term = false;
1256   FILE           *stdinToClose = NULL;
1257   FILE           *stdoutToClose = NULL;
1258   FILE           *line_editor_output;
1259 
1260   if (shell_env->magic != SHELL_MAGIC) {
1261     rtems_error(0, "invalid shell environment passed to the main loop)");
1262     return false;
1263   }
1264 
1265   if (!rtems_shell_set_shell_env(shell_env))
1266     return false;
1267 
1268   if (!rtems_shell_init_user_env()) {
1269     rtems_error(0, "rtems_shell_init_user_env");
1270     rtems_shell_clear_shell_env();
1271     return false;
1272   }
1273 
1274   shell_std_debug("env: %p\n", shell_env);
1275 
1276   if (shell_env->output == NULL || strcmp(shell_env->output, "stdout") == 0) {
1277     if (shell_env->parent_stdout != NULL)
1278       stdout = shell_env->parent_stdout;
1279   }
1280   else if (strcmp(shell_env->output, "stderr") == 0) {
1281     if (shell_env->parent_stderr != NULL)
1282       stdout = shell_env->parent_stderr;
1283     else
1284       stdout = stderr;
1285   } else if (strcmp(shell_env->output, "/dev/null") == 0) {
1286     if (stdout == NULL) {
1287       fprintf(stderr, "shell: stdout is NULLs\n");
1288       rtems_shell_clear_shell_env();
1289       return false;
1290     }
1291     fclose (stdout);
1292   } else {
1293     FILE *output = fopen(shell_env->output,
1294                          shell_env->output_append ? "a" : "w");
1295     if (output == NULL) {
1296       fprintf(stderr, "shell: open output %s failed: %s\n",
1297               shell_env->output, strerror(errno));
1298       rtems_shell_clear_shell_env();
1299       return false;
1300     }
1301     stdout = output;
1302     stdoutToClose = output;
1303   }
1304 
1305   if (shell_env->input == NULL || strcmp(shell_env->input, "stdin") == 0) {
1306     if (shell_env->parent_stdin != NULL)
1307       stdin = shell_env->parent_stdin;
1308   } else {
1309     FILE *input = fopen(shell_env->input, "r");
1310     if (input == NULL) {
1311       fprintf(stderr, "shell: open input %s failed: %s\n",
1312               shell_env->input, strerror(errno));
1313       if (stdoutToClose != NULL)
1314         fclose(stdoutToClose);
1315       rtems_shell_clear_shell_env();
1316       return false;
1317     }
1318     stdin = input;
1319     stdinToClose = input;
1320     shell_env->forever = false;
1321     interactive = false;
1322   }
1323 
1324   if (interactive) {
1325     if (stdin == NULL) {
1326       fprintf(stderr, "shell: stdin is NULLs\n");
1327       if (stdoutToClose != NULL)
1328         fclose(stdoutToClose);
1329       rtems_shell_clear_shell_env();
1330       return false;
1331     }
1332     /* Make a raw terminal, Linux Manuals */
1333     have_previous_term = (tcgetattr(fileno(stdin), &previous_term) == 0);
1334     if (have_previous_term) {
1335       term = previous_term;
1336       term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
1337       term.c_oflag &= ~OPOST;
1338       term.c_oflag |= (OPOST|ONLCR); /* But with cr+nl on output */
1339       term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
1340       term.c_cflag |= CLOCAL | CREAD;
1341       term.c_cc[VMIN]  = 1;
1342       term.c_cc[VTIME] = 0;
1343       if (tcsetattr (fileno(stdin), TCSADRAIN, &term) < 0) {
1344         fprintf(stderr,
1345                 "shell: cannot set terminal attributes(%s)\n",shell_env->devname);
1346       }
1347     }
1348   }
1349 
1350   shell_std_debug("child out: %d (%p)\n", fileno(stdout), stdout);
1351   shell_std_debug("child  in: %d (%p)\n", fileno(stdin), stdin);
1352 
1353    /* Do not buffer if interactive else leave buffered */
1354   if (interactive)
1355     setvbuf(stdin, NULL, _IONBF, 0);
1356   setvbuf(stdout, NULL, _IONBF, 0);
1357 
1358   if (isatty(fileno(stdin))) {
1359      line_editor_output = stdout;
1360   } else {
1361      line_editor_output = NULL;
1362   }
1363 
1364   result = shell_main_loop(shell_env, interactive, line_editor_output);
1365   shell_std_debug("child in-to-close: %p\n", stdinToClose);
1366   shell_std_debug("child out-to-close: %p\n", stdoutToClose);
1367 
1368   if (stdinToClose) {
1369     fclose( stdinToClose );
1370   } else if (have_previous_term) {
1371     if (tcsetattr(fileno(stdin), TCSADRAIN, &previous_term) < 0) {
1372       fprintf(
1373         stderr,
1374         "shell: cannot reset terminal attributes (%s)\n",
1375         shell_env->devname
1376       );
1377     }
1378   }
1379   if ( stdoutToClose )
1380     fclose( stdoutToClose );
1381   rtems_shell_clear_shell_env();
1382   return result;
1383 }
1384 
1385 /* ----------------------------------------------- */
1386 static rtems_status_code rtems_shell_run (
1387   const char *task_name,
1388   size_t task_stacksize,
1389   rtems_task_priority task_priority,
1390   const char *devname,
1391   bool forever,
1392   bool wait,
1393   const char *input,
1394   const char *output,
1395   bool output_append,
1396   rtems_id wake_on_end,
1397   bool echo,
1398   rtems_shell_login_check_t login_check
1399 )
1400 {
1401   rtems_id           task_id;
1402   rtems_status_code  sc;
1403   rtems_shell_env_t *shell_env;
1404   rtems_name         name;
1405 
1406   rtems_shell_init_environment();
1407 
1408   if ( task_name && strlen(task_name) >= 4)
1409     name = rtems_build_name(
1410       task_name[0], task_name[1], task_name[2], task_name[3]);
1411   else
1412     name = SHELL_MAGIC;
1413 
1414   sc = rtems_task_create(
1415     name,
1416     task_priority,
1417     task_stacksize,
1418     RTEMS_PREEMPT | RTEMS_TIMESLICE | RTEMS_NO_ASR,
1419     RTEMS_LOCAL | RTEMS_FLOATING_POINT,
1420     &task_id
1421   );
1422   if (sc != RTEMS_SUCCESSFUL) {
1423     rtems_error(sc,"creating task %s in shell_init()",task_name);
1424     return sc;
1425   }
1426 
1427   shell_env = rtems_shell_init_env( NULL );
1428   if ( !shell_env )  {
1429    rtems_error(RTEMS_NO_MEMORY,
1430                "allocating shell_env %s in shell_init()",task_name);
1431    return RTEMS_NO_MEMORY;
1432   }
1433 
1434   shell_std_debug("run: env: %p\n", shell_env);
1435 
1436   shell_env->devname       = devname;
1437   shell_env->taskname      = task_name;
1438 
1439   shell_env->exit_shell    = false;
1440   shell_env->forever       = forever;
1441   shell_env->echo          = echo;
1442   shell_env->input         = input == NULL ? NULL : strdup (input);
1443   shell_env->output        = output == NULL ? NULL : strdup (output);
1444   shell_env->output_append = output_append;
1445   shell_env->parent_stdin  = stdin;
1446   shell_env->parent_stdout = stdout;
1447   shell_env->parent_stderr = stderr;
1448   shell_env->wake_on_end   = wake_on_end;
1449   shell_env->login_check   = login_check;
1450   shell_env->uid           = getuid();
1451   shell_env->gid           = getgid();
1452 
1453   getcwd(shell_env->cwd, sizeof(shell_env->cwd));
1454 
1455   shell_std_debug("run out: %d (%p)\n",
1456                   fileno(shell_env->parent_stdout), shell_env->parent_stdout);
1457   shell_std_debug("run  in: %d (%p)\n",
1458                   fileno(shell_env->parent_stdin), shell_env->parent_stdin);
1459 
1460   sc = rtems_task_start(task_id, rtems_shell_task,
1461                         (rtems_task_argument) shell_env);
1462   if (sc != RTEMS_SUCCESSFUL) {
1463     rtems_error(sc,"starting task %s in shell_init()",task_name);
1464     free( (void*) shell_env->input );
1465     free( (void*) shell_env->output );
1466     free( shell_env );
1467     return sc;
1468   }
1469 
1470   if (wait) {
1471     rtems_event_set out;
1472     sc = rtems_event_receive (RTEMS_EVENT_1, RTEMS_WAIT, 0, &out);
1473   }
1474 
1475   shell_std_debug("run: end: sc:%d\n", sc);
1476 
1477   return sc;
1478 }
1479 
1480 rtems_status_code rtems_shell_init(
1481   const char *task_name,
1482   size_t task_stacksize,
1483   rtems_task_priority task_priority,
1484   const char *devname,
1485   bool forever,
1486   bool wait,
1487   rtems_shell_login_check_t login_check
1488 )
1489 {
1490   rtems_id to_wake = RTEMS_ID_NONE;
1491 
1492   if ( wait )
1493     to_wake = rtems_task_self();
1494 
1495   return rtems_shell_run(
1496     task_name,               /* task_name */
1497     task_stacksize,          /* task_stacksize */
1498     task_priority,           /* task_priority */
1499     devname,                 /* devname */
1500     forever,                 /* forever */
1501     wait,                    /* wait */
1502     "stdin",                 /* input */
1503     "stdout",                /* output */
1504     false,                   /* output_append */
1505     to_wake,                 /* wake_on_end */
1506     false,                   /* echo */
1507     login_check              /* login check */
1508   );
1509 }
1510 
1511 rtems_status_code rtems_shell_script (
1512   const char          *task_name,
1513   size_t               task_stacksize,
1514   rtems_task_priority  task_priority,
1515   const char*          input,
1516   const char*          output,
1517   bool                 output_append,
1518   bool                 wait,
1519   bool                 echo
1520 )
1521 {
1522   rtems_id to_wake = RTEMS_ID_NONE;
1523   rtems_status_code sc;
1524 
1525   shell_std_debug("script: in: %s out: %s\n", input, output);
1526 
1527   if ( wait )
1528     to_wake = rtems_task_self();
1529 
1530   sc = rtems_shell_run(
1531     task_name,       /* task_name */
1532     task_stacksize,  /* task_stacksize */
1533     task_priority,   /* task_priority */
1534     NULL,            /* devname */
1535     0,               /* forever */
1536     wait,            /* wait */
1537     input,           /* input */
1538     output,          /* output */
1539     output_append,   /* output_append */
1540     to_wake,         /* wake_on_end */
1541     echo,            /* echo */
1542     NULL             /* login check */
1543   );
1544 
1545   if (sc == RTEMS_SUCCESSFUL)
1546   {
1547     /* Place holder until RTEMS 5 is released then the interface for
1548      * this call will change. */
1549   }
1550 
1551   shell_std_debug("script: end: %d\n", sc);
1552 
1553   return sc;
1554 }