Back to home page

LXR

 
 

    


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

0001 /*
0002  *  Shell Script Invocation
0003  *
0004  *  Pseudo-code from Chris Johns, implemented and debugged
0005  *  by Joel Sherrill.
0006  *
0007  *  COPYRIGHT (c) 1989-2009.
0008  *  On-Line Applications Research Corporation (OAR).
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 <unistd.h>
0021 #include <string.h>
0022 #include <errno.h>
0023 #include <inttypes.h>
0024 #include <sys/types.h>
0025 #include <unistd.h>
0026 #include <limits.h>
0027 #define __need_getopt_newlib
0028 #include <getopt.h>
0029 
0030 #include <rtems.h>
0031 #include <rtems/shell.h>
0032 #include <rtems/stringto.h>
0033 #include "internal.h"
0034 
0035 static void rtems_shell_joel_usage(void)
0036 {
0037   printf(
0038     "joel [args] where args may be:\n"
0039     "  -o FILE     output file (default=stdout)\n"
0040     "  -p PRIORITY task priority\n"
0041     "  -s SIZE     task stack size\n"
0042     "  -t NAME     task name\n"
0043   );
0044 }
0045 
0046 static int findOnPATH(
0047   const char *userScriptName,
0048   char       *scriptFile,
0049   size_t      scriptFileLength
0050 )
0051 {
0052   int sc;
0053   char *cwd;
0054 
0055   /*
0056    *  If the user script name starts with a / assume it is a fully
0057    *  qualified path name and just use it.
0058    */
0059   if ( userScriptName[0] == '/' ) {
0060     strncpy( scriptFile, userScriptName, PATH_MAX );
0061   } else {
0062     /*
0063      *  For now, the provided name is just turned into a fully
0064      *  qualified path name and used.  There is no attempt to
0065      *  search along a path for it.
0066      */
0067 
0068     /* XXX should use strncat but what is the limit? */
0069     cwd = getcwd( scriptFile, PATH_MAX );
0070     if ( cwd != NULL ) {
0071       int cwdlen = strnlen( scriptFile, PATH_MAX );
0072 
0073       strncat( scriptFile, "/", PATH_MAX - cwdlen );
0074       strncat(
0075           scriptFile,
0076           ( (userScriptName[0] == '.' && userScriptName[1] == '/') ?
0077             &userScriptName[2] : userScriptName),
0078           PATH_MAX - cwdlen - 1
0079           );
0080     } else {
0081       return -1;
0082     }
0083   }
0084 
0085   sc = access( scriptFile, R_OK );
0086   if ( sc ) {
0087     return -1;
0088   }
0089 
0090   return 0;
0091 
0092 #if 0
0093    /*
0094     * Does the command (argv[0]) contain a path ?, i.e. starts with
0095     * '.' or contains a '/'?
0096     */
0097    /* TODO: Add concept of PATH */
0098    if (!contains_path) {
0099      /* check PATH environment variable */
0100      for (path_part = PATH; path_part; skip to ':')
0101      {
0102      }
0103      if (not found)
0104        return -1;
0105    }
0106 #endif
0107 }
0108 
0109 static int rtems_shell_main_joel(
0110   int    argc,
0111   char **argv
0112 )
0113 {
0114   unsigned long        tmp;
0115   int                  option;
0116   int                  sc;
0117   int                  verbose = 0;
0118   char                *taskName = "JOEL";
0119   uint32_t             stackSize = RTEMS_MINIMUM_STACK_SIZE * 10;
0120   rtems_task_priority  taskPriority = 20;
0121   char                *outputFile = "stdout";
0122   rtems_status_code    result;
0123   char                 scriptFile[PATH_MAX];
0124   struct getopt_data   getopt_reent;
0125 
0126   memset(&getopt_reent, 0, sizeof(getopt_data));
0127   while ( (option = getopt_r( argc, argv, "o:p:s:t:v", &getopt_reent)) != -1 ) {
0128     switch ((char)option) {
0129       case 'o':
0130         outputFile = getopt_reent.optarg;
0131         break;
0132       case 'p': {
0133         const char *s = getopt_reent.optarg;
0134 
0135     if ( rtems_string_to_unsigned_long( s, &tmp, NULL, 0) ) {
0136       printf( "Task Priority argument (%s) is not a number\n", s );
0137       return -1;
0138     }
0139     taskPriority = (rtems_task_priority) tmp;
0140         break;
0141       }
0142       case 's': {
0143         const char *s = getopt_reent.optarg;
0144 
0145     if ( rtems_string_to_unsigned_long( s, &tmp, NULL, 0) ) {
0146       printf( "Stack size argument (%s) is not a number\n", s );
0147       return -1;
0148     }
0149         stackSize = (uint32_t) tmp;
0150         break;
0151       }
0152       case 't':
0153         taskName = getopt_reent.optarg;
0154         break;
0155       case 'v':
0156         verbose = 1;
0157         break;
0158       case '?':
0159       default:
0160         rtems_shell_joel_usage();
0161         return -1;
0162     }
0163   }
0164 
0165   if ( verbose ) {
0166     fprintf( stderr,
0167       "outputFile: %s\n"
0168       "taskPriority: %" PRId32 "\n"
0169       "stackSize: %" PRId32 "\n"
0170       "taskName: %s\n",
0171       outputFile,
0172       taskPriority,
0173       stackSize,
0174       taskName
0175    );
0176   }
0177 
0178   /*
0179    *  Verify there is a script name past the end of the arguments.
0180    *  Preincrement to skip program name.
0181    */
0182   if ( getopt_reent.optind >= argc ) {
0183     fprintf( stderr, "Shell: No script to execute\n" );
0184     return -1;
0185   }
0186 
0187   /*
0188    *  Find script on the path.
0189    *
0190    *  NOTE: It is terrible that this is done twice but it
0191    *        seems to be the most expedient thing.
0192    */
0193   sc = findOnPATH( argv[getopt_reent.optind], scriptFile, PATH_MAX );
0194   if ( sc ) {
0195     fprintf( stderr, "%s: command not found\n", argv[0] );
0196     return -1;
0197   }
0198 
0199   /* fprintf( stderr, "SCRIPT: -%s-\n", scriptFile ); */
0200 
0201   /*
0202    *  I assume that argv[optind...] will have the arguments to
0203    *  the shell script.  But that remains to be implemented.
0204    */
0205 
0206   /*
0207    * Run the script
0208    */
0209   result = rtems_shell_script(
0210     taskName,        /* the name of the task */
0211     stackSize,       /* stack size */
0212     taskPriority,    /* task priority */
0213     scriptFile,      /* the script file */
0214     outputFile,      /* where to redirect the script */
0215     0,               /* run once and exit */
0216     1,               /* we will wait */
0217     verbose          /* do we echo */
0218   );
0219   if (result)
0220     return -1;
0221   return 0;
0222 }
0223 
0224 rtems_shell_cmd_t rtems_shell_JOEL_Command = {
0225   "joel",                        /* name */
0226   "joel [args] SCRIPT",          /* usage */
0227   "misc",                        /* topic */
0228   rtems_shell_main_joel,         /* command */
0229   NULL,                          /* alias */
0230   NULL                           /* next */
0231 };
0232 
0233 /*
0234  *  This is a helper function which takes a command as arguments
0235  *  which has not been located as a built-in command and attempts
0236  *  to find something in the filesystem with the same name that
0237  *  appears to be a shell script.
0238  */
0239 int rtems_shell_script_file(
0240   int   argc RTEMS_UNUSED,
0241   char *argv[]
0242 )
0243 {
0244   #define FIRST_LINE_LENGTH 128
0245   #define SCRIPT_ARGV_LIMIT 32
0246   char    scriptFile[PATH_MAX];
0247   char   *scriptHead;
0248   char    scriptHeadBuffer[FIRST_LINE_LENGTH];
0249   int     sc;
0250   FILE   *script;
0251   size_t  length;
0252   int     scriptArgc;
0253   char   *scriptArgv[SCRIPT_ARGV_LIMIT];
0254 
0255   /*
0256    *  Clear argv pointer array
0257    */
0258   for ( scriptArgc=0 ; scriptArgc<SCRIPT_ARGV_LIMIT ; scriptArgc++ )
0259     scriptArgv[scriptArgc] = NULL;
0260 
0261   /*
0262    *  Find argv[0] on the path
0263    */
0264   sc = findOnPATH( argv[0], scriptFile, PATH_MAX );
0265   if ( sc ) {
0266     fprintf( stderr, "%s: command not found\n", argv[0] );
0267     return -1;
0268   }
0269 
0270   /*
0271    *  Open the file so we can see if it looks like a script.
0272    */
0273   script = fopen( scriptFile, "r" );
0274   if ( !script ) {
0275     fprintf( stderr, "%s: Unable to open %s\n", argv[0], scriptFile );
0276     return -1;
0277   }
0278 
0279   /*
0280    *  Is the script OK to run?
0281    *  Verify the current user has permission to execute it.
0282    *
0283    *  NOTE: May not work on all file systems
0284    */
0285   sc = access( scriptFile, X_OK );
0286   if ( sc ) {
0287     fprintf( stderr, "Unable to execute %s\n", scriptFile );
0288     fclose( script );
0289     return -1;
0290   }
0291 
0292   /*
0293    *  Try to read the first line from the potential script file
0294    */
0295   scriptHead = fgets(scriptHeadBuffer, FIRST_LINE_LENGTH, script);
0296   if ( !scriptHead ) {
0297     fprintf(
0298       stderr, "%s: Unable to read first line of %s\n", argv[0], scriptFile );
0299     fclose( script );
0300     return -1;
0301   }
0302 
0303   fclose(script);
0304 
0305   length = strnlen(scriptHead, FIRST_LINE_LENGTH);
0306   scriptHead[length - 1] = '\0';
0307 
0308   /* fprintf( stderr, "FIRST LINE: -%s-\n", scriptHead ); */
0309 
0310   /*
0311    *  Verify the name of the "shell" is joel.  This means
0312    *  the line starts with "#! joel".
0313    */
0314   if (strncmp("#! joel", scriptHead, 7) != 0) {
0315     fprintf( stderr, "%s: Not a joel script %s\n", argv[0], scriptFile );
0316     return -1;
0317   }
0318 
0319   /*
0320    * Do not worry about search path further.  We have found the
0321    * script, it is executable, and we have successfully read the
0322    * first line and found out it is a script.
0323    */
0324 
0325   /*
0326    * Check for arguments in the first line of the script.  This changes
0327    * how the shell task is run.
0328    */
0329 
0330   sc = rtems_shell_make_args(
0331     &scriptHead[3],
0332     &scriptArgc,
0333     scriptArgv,
0334     SCRIPT_ARGV_LIMIT - 1
0335   );
0336   if ( sc ) {
0337     fprintf(
0338       stderr, "%s: Error parsing joel arguments %s\n", argv[0], scriptFile );
0339     return -1;
0340   }
0341 
0342   scriptArgv[ scriptArgc++ ] = scriptFile;
0343 
0344   /*
0345    *  TODO: How do we pass arguments from here to the script?
0346    *        At this point, it doesn't matter because we don't
0347    *        have any way for a shell script to access them.
0348    */
0349   return rtems_shell_main_joel( scriptArgc, scriptArgv );
0350 }