Files
rtems/cpukit/libmisc/shell/shell_script.c
Joel Sherrill 68baf9eead libmisc/shell/*: Relicense to 2-BSD
Many files in this directory were by persons and organizations
that have previously given relicensing permission. git history
was used to confirm authorship and contributions on files which
had unclear or missing copyrights.

Update #3053.
2025-03-01 20:21:56 +00:00

370 lines
9.6 KiB
C

/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
* @brief Shell Script Invocation Implementation
*/
/*
* COPYRIGHT (c) 1989-2009.
* On-Line Applications Research Corporation (OAR).
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <inttypes.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#define __need_getopt_newlib
#include <getopt.h>
#include <rtems.h>
#include <rtems/shell.h>
#include <rtems/stringto.h>
#include "internal.h"
static void rtems_shell_joel_usage(void)
{
printf(
"joel [args] where args may be:\n"
" -o FILE output file (default=stdout)\n"
" -p PRIORITY task priority\n"
" -s SIZE task stack size\n"
" -t NAME task name\n"
);
}
static int findOnPATH(
const char *userScriptName,
char *scriptFile,
size_t scriptFileLength
)
{
int sc;
char *cwd;
/*
* If the user script name starts with a / assume it is a fully
* qualified path name and just use it.
*/
if ( userScriptName[0] == '/' ) {
strncpy( scriptFile, userScriptName, PATH_MAX );
} else {
/*
* For now, the provided name is just turned into a fully
* qualified path name and used. There is no attempt to
* search along a path for it.
*/
/* XXX should use strncat but what is the limit? */
cwd = getcwd( scriptFile, PATH_MAX );
if ( cwd != NULL ) {
int cwdlen = strnlen( scriptFile, PATH_MAX );
strncat( scriptFile, "/", PATH_MAX - cwdlen );
strncat(
scriptFile,
( (userScriptName[0] == '.' && userScriptName[1] == '/') ?
&userScriptName[2] : userScriptName),
PATH_MAX - cwdlen - 1
);
} else {
return -1;
}
}
sc = access( scriptFile, R_OK );
if ( sc ) {
return -1;
}
return 0;
#if 0
/*
* Does the command (argv[0]) contain a path ?, i.e. starts with
* '.' or contains a '/'?
*/
/* TODO: Add concept of PATH */
if (!contains_path) {
/* check PATH environment variable */
for (path_part = PATH; path_part; skip to ':')
{
}
if (not found)
return -1;
}
#endif
}
static int rtems_shell_main_joel(
int argc,
char **argv
)
{
unsigned long tmp;
int option;
int sc;
int verbose = 0;
char *taskName = "JOEL";
uint32_t stackSize = RTEMS_MINIMUM_STACK_SIZE * 10;
rtems_task_priority taskPriority = 20;
char *outputFile = "stdout";
rtems_status_code result;
char scriptFile[PATH_MAX];
struct getopt_data getopt_reent;
memset(&getopt_reent, 0, sizeof(getopt_data));
while ( (option = getopt_r( argc, argv, "o:p:s:t:v", &getopt_reent)) != -1 ) {
switch ((char)option) {
case 'o':
outputFile = getopt_reent.optarg;
break;
case 'p': {
const char *s = getopt_reent.optarg;
if ( rtems_string_to_unsigned_long( s, &tmp, NULL, 0) ) {
printf( "Task Priority argument (%s) is not a number\n", s );
return -1;
}
taskPriority = (rtems_task_priority) tmp;
break;
}
case 's': {
const char *s = getopt_reent.optarg;
if ( rtems_string_to_unsigned_long( s, &tmp, NULL, 0) ) {
printf( "Stack size argument (%s) is not a number\n", s );
return -1;
}
stackSize = (uint32_t) tmp;
break;
}
case 't':
taskName = getopt_reent.optarg;
break;
case 'v':
verbose = 1;
break;
case '?':
default:
rtems_shell_joel_usage();
return -1;
}
}
if ( verbose ) {
fprintf( stderr,
"outputFile: %s\n"
"taskPriority: %" PRId32 "\n"
"stackSize: %" PRId32 "\n"
"taskName: %s\n",
outputFile,
taskPriority,
stackSize,
taskName
);
}
/*
* Verify there is a script name past the end of the arguments.
* Preincrement to skip program name.
*/
if ( getopt_reent.optind >= argc ) {
fprintf( stderr, "Shell: No script to execute\n" );
return -1;
}
/*
* Find script on the path.
*
* NOTE: It is terrible that this is done twice but it
* seems to be the most expedient thing.
*/
sc = findOnPATH( argv[getopt_reent.optind], scriptFile, PATH_MAX );
if ( sc ) {
fprintf( stderr, "%s: command not found\n", argv[0] );
return -1;
}
/* fprintf( stderr, "SCRIPT: -%s-\n", scriptFile ); */
/*
* I assume that argv[optind...] will have the arguments to
* the shell script. But that remains to be implemented.
*/
/*
* Run the script
*/
result = rtems_shell_script(
taskName, /* the name of the task */
stackSize, /* stack size */
taskPriority, /* task priority */
scriptFile, /* the script file */
outputFile, /* where to redirect the script */
0, /* run once and exit */
1, /* we will wait */
verbose /* do we echo */
);
if (result)
return -1;
return 0;
}
rtems_shell_cmd_t rtems_shell_JOEL_Command = {
"joel", /* name */
"joel [args] SCRIPT", /* usage */
"misc", /* topic */
rtems_shell_main_joel, /* command */
NULL, /* alias */
NULL /* next */
};
/*
* This is a helper function which takes a command as arguments
* which has not been located as a built-in command and attempts
* to find something in the filesystem with the same name that
* appears to be a shell script.
*/
int rtems_shell_script_file(
int argc RTEMS_UNUSED,
char *argv[]
)
{
#define FIRST_LINE_LENGTH 128
#define SCRIPT_ARGV_LIMIT 32
char scriptFile[PATH_MAX];
char *scriptHead;
char scriptHeadBuffer[FIRST_LINE_LENGTH];
int sc;
FILE *script;
size_t length;
int scriptArgc;
char *scriptArgv[SCRIPT_ARGV_LIMIT];
/*
* Clear argv pointer array
*/
for ( scriptArgc=0 ; scriptArgc<SCRIPT_ARGV_LIMIT ; scriptArgc++ )
scriptArgv[scriptArgc] = NULL;
/*
* Find argv[0] on the path
*/
sc = findOnPATH( argv[0], scriptFile, PATH_MAX );
if ( sc ) {
fprintf( stderr, "%s: command not found\n", argv[0] );
return -1;
}
/*
* Open the file so we can see if it looks like a script.
*/
script = fopen( scriptFile, "r" );
if ( !script ) {
fprintf( stderr, "%s: Unable to open %s\n", argv[0], scriptFile );
return -1;
}
/*
* Is the script OK to run?
* Verify the current user has permission to execute it.
*
* NOTE: May not work on all file systems
*/
sc = access( scriptFile, X_OK );
if ( sc ) {
fprintf( stderr, "Unable to execute %s\n", scriptFile );
fclose( script );
return -1;
}
/*
* Try to read the first line from the potential script file
*/
scriptHead = fgets(scriptHeadBuffer, FIRST_LINE_LENGTH, script);
if ( !scriptHead ) {
fprintf(
stderr, "%s: Unable to read first line of %s\n", argv[0], scriptFile );
fclose( script );
return -1;
}
fclose(script);
length = strnlen(scriptHead, FIRST_LINE_LENGTH);
scriptHead[length - 1] = '\0';
/* fprintf( stderr, "FIRST LINE: -%s-\n", scriptHead ); */
/*
* Verify the name of the "shell" is joel. This means
* the line starts with "#! joel".
*/
if (strncmp("#! joel", scriptHead, 7) != 0) {
fprintf( stderr, "%s: Not a joel script %s\n", argv[0], scriptFile );
return -1;
}
/*
* Do not worry about search path further. We have found the
* script, it is executable, and we have successfully read the
* first line and found out it is a script.
*/
/*
* Check for arguments in the first line of the script. This changes
* how the shell task is run.
*/
sc = rtems_shell_make_args(
&scriptHead[3],
&scriptArgc,
scriptArgv,
SCRIPT_ARGV_LIMIT - 1
);
if ( sc ) {
fprintf(
stderr, "%s: Error parsing joel arguments %s\n", argv[0], scriptFile );
return -1;
}
scriptArgv[ scriptArgc++ ] = scriptFile;
/*
* TODO: How do we pass arguments from here to the script?
* At this point, it doesn't matter because we don't
* have any way for a shell script to access them.
*/
return rtems_shell_main_joel( scriptArgc, scriptArgv );
}