aboutsummaryrefslogtreecommitdiff
path: root/jim-exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'jim-exec.c')
-rw-r--r--jim-exec.c435
1 files changed, 264 insertions, 171 deletions
diff --git a/jim-exec.c b/jim-exec.c
index 343d5e1..0129291 100644
--- a/jim-exec.c
+++ b/jim-exec.c
@@ -97,8 +97,8 @@ struct WaitInfoTable;
static char **JimOriginalEnviron(void);
static char **JimSaveEnv(char **env);
static void JimRestoreEnv(char **env);
-static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
- phandle_t **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr);
+static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t **pidArrayPtr,
+ int *outPipePtr, int *errFilePtr);
static void JimDetachPids(struct WaitInfoTable *table, int numPids, const phandle_t *pidPtr);
static int JimCleanupChildren(Jim_Interp *interp, int numPids, phandle_t *pidPtr, Jim_Obj *errStrObj);
static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
@@ -383,7 +383,7 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
int i;
argc--;
- numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
+ numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL);
if (numPids < 0) {
return JIM_ERR;
}
@@ -402,7 +402,7 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
* Create the command's pipeline.
*/
numPids =
- JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);
+ JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, &outputId, &errorId);
if (numPids < 0) {
return JIM_ERR;
@@ -636,37 +636,138 @@ static int Jim_PidCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
return JIM_OK;
}
-/*
- *----------------------------------------------------------------------
- *
- * JimCreatePipeline --
- *
- * Given an argc/argv array, instantiate a pipeline of processes
- * as described by the argv.
- *
- * Results:
- * The return value is a count of the number of new processes
- * created, or -1 if an error occurred while creating the pipeline.
- * *pidArrayPtr is filled in with the address of a dynamically
- * allocated array giving the ids of all of the processes. It
- * is up to the caller to free this array when it isn't needed
- * anymore. If inPipePtr is non-NULL, *inPipePtr is filled in
- * with the file id for the input pipe for the pipeline (if any):
- * the caller must eventually close this file. If outPipePtr
- * isn't NULL, then *outPipePtr is filled in with the file id
- * for the output pipe from the pipeline: the caller must close
- * this file. If errFilePtr isn't NULL, then *errFilePtr is filled
- * with a file id that may be used to read error output after the
- * pipeline completes.
- *
- * Side effects:
- * Processes and pipes are created.
+#define JIM_ETT_IN 0x0001 /* < */
+#define JIM_ETT_OUT 0x0002 /* > */
+#define JIM_ETT_ERR 0x0004 /* 2> */
+#define JIM_ETT_PIPE 0x0008 /* | */
+
+#define JIM_ETT_NOARG 0x0010 /* does not accept an additional argument */
+#define JIM_ETT_APPEND 0x0020 /* append to output */
+#define JIM_ETT_STR 0x0040 /* arg is a literal */
+#define JIM_ETT_DUPERR 0x0080 /* dup output to err */
+#define JIM_ETT_HANDLE 0x0100 /* arg is a filehandle */
+
+#define JIM_ETT_CMD 0xF000
+#define JIM_ETT_BAD 0xF001
+
+struct redir_type_t {
+ const char *prefix;
+ unsigned flags;
+};
+
+/* These need to be sorted by length, most specific first */
+static const struct redir_type_t redir_types[] = {
+ { "<<@", JIM_ETT_IN | JIM_ETT_HANDLE | JIM_ETT_STR },
+ { "<<", JIM_ETT_IN | JIM_ETT_STR },
+ { "<@", JIM_ETT_IN | JIM_ETT_HANDLE },
+ { "<", JIM_ETT_IN },
+
+ { "2>>", JIM_ETT_ERR | JIM_ETT_APPEND },
+ { "2>@", JIM_ETT_ERR | JIM_ETT_HANDLE },
+ { "2>", JIM_ETT_ERR },
+
+ { ">>&", JIM_ETT_OUT | JIM_ETT_APPEND | JIM_ETT_DUPERR },
+ { ">>", JIM_ETT_OUT | JIM_ETT_APPEND },
+ { ">&@", JIM_ETT_OUT | JIM_ETT_HANDLE | JIM_ETT_DUPERR },
+ { ">@", JIM_ETT_OUT | JIM_ETT_HANDLE },
+ { ">&", JIM_ETT_OUT | JIM_ETT_DUPERR },
+ { ">", JIM_ETT_OUT },
+
+ { "|&", JIM_ETT_PIPE | JIM_ETT_DUPERR },
+ { "|", JIM_ETT_PIPE },
+ { NULL }
+};
+
+static unsigned JimExecClassifyArg(const char *arg)
+{
+ int i;
+ for (i = 0; redir_types[i].prefix; i++) {
+ int len = strlen(redir_types[i].prefix);
+ if (strncmp(arg, redir_types[i].prefix, len) == 0) {
+ if (strlen(arg) > len) {
+ if (redir_types[i].flags & JIM_ETT_NOARG) {
+ /* error - no arg expected */
+ return JIM_ETT_BAD;
+ }
+ return redir_types[i].flags;
+ }
+ /* Token doesn't contain an arg */
+ return redir_types[i].flags | JIM_ETT_NOARG;
+ }
+ }
+ return JIM_ETT_CMD;
+}
+
+/**
+ * Parses the exec pipeline in legacy format into two lists, cmdList and redirectList.
+ * (These must start as empty lists)
+ *
+ * cmdList contains a list of {cmdlist ?sep cmdlist ...? }
+ * i.e. pairs of cmdlist (a list of {command arg...}) and a separator: | or |&
+ * with the separator missing after the last command list.
*
- *----------------------------------------------------------------------
+ * Returns JIM_OK if ok or JIM_ERR on error.
*/
-static int
-JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t **pidArrayPtr,
- int *inPipePtr, int *outPipePtr, int *errFilePtr)
+static int JimParsePipelineLegacy(Jim_Interp *interp, int argc, Jim_Obj *const *argv, Jim_Obj *cmdList, Jim_Obj *redirectList)
+{
+ int i;
+ /* Add an initial empty commandlist */
+ Jim_Obj *cmdObj = Jim_NewListObj(interp, NULL, 0);
+ Jim_ListAppendElement(interp, cmdList, cmdObj);
+ const char *arg = NULL;
+
+ for (i = 0; i < argc; i++) {
+ arg = Jim_String(argv[i]);
+ unsigned ett = JimExecClassifyArg(arg);
+ if (ett == JIM_ETT_BAD) {
+ Jim_SetResultFormatted(interp, "invalid: %s", arg);
+ return JIM_ERR;
+ }
+ if (ett == JIM_ETT_CMD) {
+ /* Add to the current command */
+ Jim_ListAppendElement(interp, cmdObj, argv[i]);
+ continue;
+ }
+ if (ett & JIM_ETT_PIPE) {
+ if (Jim_ListLength(interp, cmdObj) == 0) {
+ goto missing_cmd;
+ }
+ /* Add this separator */
+ Jim_ListAppendElement(interp, cmdList, argv[i]);
+ /* Now start a new command list */
+ cmdObj = Jim_NewListObj(interp, NULL, 0);
+ Jim_ListAppendElement(interp, cmdList, cmdObj);
+ continue;
+ }
+ Jim_ListAppendElement(interp, redirectList, argv[i]);
+ if ((ett & JIM_ETT_NOARG)) {
+ /* This means we need an arg */
+ if (i >= argc - 1) {
+ /* This is an error */
+ Jim_SetResultFormatted(interp, "can't specify \"%#s\" as last word in command", argv[i]);
+ return -1;
+ }
+ i++;
+ Jim_ListAppendElement(interp, redirectList, argv[i]);
+ }
+ }
+
+ if (Jim_ListLength(interp, cmdObj) == 0) {
+missing_cmd:
+ if (arg && *arg == '|') {
+ Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
+ }
+ else {
+ Jim_SetResultString(interp, "didn't specify command to execute", -1);
+ }
+ return JIM_ERR;
+ }
+
+ return JIM_OK;
+}
+
+static int JimExecPipeline(Jim_Interp *interp, Jim_Obj *cmdList, Jim_Obj *redirectList,
+ phandle_t **pidArrayPtr, int *outPipePtr, int *errFilePtr)
{
phandle_t *pidPtr = NULL; /* Points to alloc-ed array holding all
* the pids of child processes. */
@@ -679,9 +780,9 @@ JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t
* from stdin/pipe. */
int input_len = 0; /* Length of input, if relevant */
-#define FILE_NAME 0 /* input/output: filename */
+#define FILE_NAME 0 /* input/output: filename or @filehandle */
#define FILE_APPEND 1 /* output only: filename, append */
-#define FILE_HANDLE 2 /* input/output: filehandle */
+#define FILE_HANDLE 2 /* input/output: @ filehandle */
#define FILE_TEXT 3 /* input only: input is actual text */
int inputFile = FILE_NAME; /* 1 means input is name of input file.
@@ -720,9 +821,6 @@ JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t
* in pipeline (could be file or pipe).
* -1 means use stdout. */
int pipeIds[2]; /* File ids for pipe that's being created. */
- int firstArg, lastArg; /* Indexes of first and last arguments in
- * current command. */
- int lastBar;
int i;
phandle_t phandle;
char **save_environ;
@@ -731,13 +829,6 @@ JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t
#endif
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
- /* Holds the args which will be used to exec */
- char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
- int arg_count = 0;
-
- if (inPipePtr != NULL) {
- *inPipePtr = -1;
- }
if (outPipePtr != NULL) {
*outPipePtr = -1;
}
@@ -746,105 +837,72 @@ JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t
}
pipeIds[0] = pipeIds[1] = -1;
- /*
- * First, scan through all the arguments to figure out the structure
- * of the pipeline. Count the number of distinct processes (it's the
- * number of "|" arguments). If there are "<", "<<", or ">" arguments
- * then make note of input and output redirection and remove these
- * arguments and the arguments that follow them.
+ /* Now interpet the redirection list
*/
- cmdCount = 1;
- lastBar = -1;
- for (i = 0; i < argc; i++) {
- const char *arg = Jim_String(argv[i]);
-
- if (arg[0] == '<') {
- inputFile = FILE_NAME;
- input = arg + 1;
- if (*input == '<') {
- inputFile = FILE_TEXT;
- input_len = Jim_Length(argv[i]) - 2;
- input++;
- }
- else if (*input == '@') {
- inputFile = FILE_HANDLE;
- input++;
- }
-
- if (!*input && ++i < argc) {
- input = Jim_GetString(argv[i], &input_len);
- }
- }
- else if (arg[0] == '>') {
- int dup_error = 0;
-
- outputFile = FILE_NAME;
-
- output = arg + 1;
- if (*output == '>') {
- outputFile = FILE_APPEND;
- output++;
- }
- if (*output == '&') {
- /* Redirect stderr too */
- output++;
- dup_error = 1;
- }
- if (*output == '@') {
- outputFile = FILE_HANDLE;
- output++;
- }
- if (!*output && ++i < argc) {
- output = Jim_String(argv[i]);
+ int redir_len = Jim_ListLength(interp, redirectList);
+ for (i = 0; i < redir_len; i++) {
+ int len;
+ int item_len;
+ Jim_Obj *redirObj = Jim_ListGetIndex(interp, redirectList, i);
+ const char *arg = Jim_GetString(redirObj, &len);
+ unsigned ett = JimExecClassifyArg(arg);
+ const char *item;
+ int type = FILE_NAME;
+ if ((ett & JIM_ETT_NOARG) == 0) {
+ /* No separate arg. Need to skip over the appropriate number or redirection chars */
+ item = arg + 1;
+ if ((ett & JIM_ETT_HANDLE)) {
+ item++;
}
- if (dup_error) {
- errorFile = outputFile;
- error = output;
+ if ((ett & JIM_ETT_APPEND)) {
+ item++;
}
- }
- else if (arg[0] == '2' && arg[1] == '>') {
- error = arg + 2;
- errorFile = FILE_NAME;
-
- if (*error == '@') {
- errorFile = FILE_HANDLE;
- error++;
+ if ((ett & JIM_ETT_DUPERR)) {
+ item++;
}
- else if (*error == '>') {
- errorFile = FILE_APPEND;
- error++;
+ if ((ett & JIM_ETT_ERR)) {
+ item++;
}
- if (!*error && ++i < argc) {
- error = Jim_String(argv[i]);
+ if ((ett & JIM_ETT_STR)) {
+ type = FILE_TEXT;
+ item++;
}
+ item_len = len - (item - arg);
}
else {
- if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) {
- if (i == lastBar + 1 || i == argc - 1) {
- Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
- goto badargs;
- }
- lastBar = i;
- cmdCount++;
+ /* separate arg, so fetch it */
+ i++;
+ item = Jim_GetString(Jim_ListGetIndex(interp, redirectList, i), &item_len);
+ }
+ /* Figure out the type */
+ if ((ett & JIM_ETT_HANDLE)) {
+ type = FILE_HANDLE;
+ }
+ if ((ett & JIM_ETT_APPEND)) {
+ type = FILE_APPEND;
+ }
+ if ((ett & JIM_ETT_STR)) {
+ type = FILE_TEXT;
+ }
+ if (ett & JIM_ETT_IN) {
+ input = item;
+ input_len = item_len;
+ inputFile = type;
+ }
+ else if (ett & JIM_ETT_OUT) {
+ output = item;
+ outputFile = type;
+ if (ett & JIM_ETT_DUPERR) {
+ error = output;
+ errorFile = outputFile;
}
- /* Either |, |& or a "normal" arg, so store it in the arg array */
- arg_array[arg_count++] = (char *)arg;
- continue;
}
-
- if (i >= argc) {
- Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg);
- goto badargs;
+ else if (ett & JIM_ETT_ERR) {
+ error = item;
+ errorFile = type;
}
}
- if (arg_count == 0) {
- Jim_SetResultString(interp, "didn't specify command to execute", -1);
-badargs:
- Jim_Free(arg_array);
- return -1;
- }
-
/* Must do this before vfork(), so do it now */
save_environ = JimSaveEnv(JimBuildEnv(interp));
@@ -869,7 +927,8 @@ badargs:
}
Jim_Lseek(inputId, 0L, SEEK_SET);
}
- else if (inputFile == FILE_HANDLE) {
+ else if (inputFile == FILE_HANDLE || *input == '@') {
+ input += (inputFile == FILE_NAME);
int fd = JimGetChannelFd(interp, input);
if (fd < 0) {
@@ -888,15 +947,6 @@ badargs:
}
}
}
- else if (inPipePtr != NULL) {
- if (pipe(pipeIds) != 0) {
- Jim_SetResultErrno(interp, "couldn't create input pipe for command");
- goto error;
- }
- inputId = pipeIds[0];
- *inPipePtr = pipeIds[1];
- pipeIds[0] = pipeIds[1] = -1;
- }
/*
* Set up the redirected output sink for the pipeline from one
@@ -936,6 +986,7 @@ badargs:
/* If we are redirecting stderr with 2>filename or 2>@fileId, then we ignore errFilePtr */
if (error != NULL) {
if (errorFile == FILE_HANDLE) {
+ error += (errorFile == FILE_NAME);
if (strcmp(error, "1") == 0) {
/* Special 2>@1 */
if (lastOutputId != -1) {
@@ -982,46 +1033,46 @@ badargs:
}
/*
- * Scan through the argc array, forking off a process for each
- * group of arguments between "|" arguments.
+ * Iterate over cmdList, forking off a process for each
+ * cmdlist
*/
-
+ int cmd_list_size = Jim_ListLength(interp, cmdList);
+ cmdCount = (cmd_list_size + 1) / 2;
pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
- for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
- int pipe_dup_err = 0;
- int origErrorId = errorId;
- for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
- if (strcmp(arg_array[lastArg], "|") == 0) {
- break;
- }
- if (strcmp(arg_array[lastArg], "|&") == 0) {
- pipe_dup_err = 1;
- break;
- }
+ for (i = 0; i < cmd_list_size; ) {
+ char **arg_array;
+ int j;
+ int origErrorId = errorId;
+ Jim_Obj *cmdObj = Jim_ListGetIndex(interp, cmdList, i++);
+ int cmd_len = Jim_ListLength(interp, cmdObj);
+ Jim_Obj *sepObj = NULL;
+ if (i < cmd_list_size - 1) {
+ sepObj = Jim_ListGetIndex(interp, cmdList, i++);
}
- if (lastArg == firstArg) {
- Jim_SetResultString(interp, "missing command to exec", -1);
- goto error;
+ /* Build exec array */
+ arg_array = Jim_Alloc((cmd_len + 1) * sizeof(*arg_array));
+ for (j = 0; j < cmd_len; j++) {
+ arg_array[j] = (char *)Jim_String(Jim_ListGetIndex(interp, cmdObj, j));
}
+ arg_array[j] = NULL;
- /* Replace | with NULL for execv() */
- arg_array[lastArg] = NULL;
- if (lastArg == arg_count) {
+ if (sepObj == NULL) {
outputId = lastOutputId;
lastOutputId = -1;
}
else {
if (pipe(pipeIds) != 0) {
Jim_SetResultErrno(interp, "couldn't create pipe");
+ Jim_Free(arg_array);
goto error;
}
outputId = pipeIds[1];
}
/* Need to do this before vfork() */
- if (pipe_dup_err) {
+ if (sepObj && Jim_CompareStringImmediate(interp, sepObj, "|&")) {
errorId = outputId;
}
@@ -1034,7 +1085,7 @@ badargs:
goto error;
}
#else
- i = strlen(arg_array[firstArg]);
+ int argv0_len = strlen(arg_array[0]);
#ifdef HAVE_EXECVPE
child_environ = Jim_GetEnviron();
@@ -1083,10 +1134,10 @@ badargs:
close(lastOutputId);
}
- execvpe(arg_array[firstArg], &arg_array[firstArg], child_environ);
+ execvpe(arg_array[0], arg_array, child_environ);
if (write(fileno(stderr), "couldn't exec \"", 15) &&
- write(fileno(stderr), arg_array[firstArg], i) &&
+ write(fileno(stderr), arg_array[0], argv0_len) &&
write(fileno(stderr), "\"\n", 2)) {
/* nothing */
}
@@ -1102,6 +1153,7 @@ badargs:
#endif
/* parent */
+ Jim_Free(arg_array);
/*
* Enlarge the wait table if there isn't enough space for a new
@@ -1116,7 +1168,7 @@ badargs:
table->info[table->used].flags = 0;
table->used++;
- pidPtr[numPids] = phandle;
+ pidPtr[numPids++] = phandle;
/* Restore in case of pipe_dup_err */
errorId = origErrorId;
@@ -1151,7 +1203,6 @@ badargs:
if (errorId != -1) {
close(errorId);
}
- Jim_Free(arg_array);
JimRestoreEnv(save_environ);
@@ -1164,10 +1215,6 @@ badargs:
*/
error:
- if ((inPipePtr != NULL) && (*inPipePtr != -1)) {
- close(*inPipePtr);
- *inPipePtr = -1;
- }
if ((outPipePtr != NULL) && (*outPipePtr != -1)) {
close(*outPipePtr);
*outPipePtr = -1;
@@ -1197,6 +1244,52 @@ badargs:
/*
*----------------------------------------------------------------------
*
+ * JimCreatePipeline --
+ *
+ * Given an argc/argv array, instantiate a pipeline of processes
+ * as described by the argv.
+ *
+ * Results:
+ * The return value is a count of the number of new processes
+ * created, or -1 if an error occurred while creating the pipeline.
+ * *pidArrayPtr is filled in with the address of a dynamically
+ * allocated array giving the ids of all of the processes. It
+ * is up to the caller to free this array when it isn't needed
+ * anymore. If outPipePtr
+ * isn't NULL, then *outPipePtr is filled in with the file id
+ * for the output pipe from the pipeline: the caller must close
+ * this file. If errFilePtr isn't NULL, then *errFilePtr is filled
+ * with a file id that may be used to read error output after the
+ * pipeline completes.
+ *
+ * Side effects:
+ * Processes and pipes are created.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t **pidArrayPtr,
+ int *outPipePtr, int *errFilePtr)
+{
+ /* JimParsePipelineLegacy builds cmdList and redirectList */
+ Jim_Obj *cmdList = Jim_NewListObj(interp, NULL, 0);
+ Jim_Obj *redirectList = Jim_NewListObj(interp, NULL, 0);
+ Jim_IncrRefCount(cmdList);
+ Jim_IncrRefCount(redirectList);
+
+ int rc = -1;
+ if (JimParsePipelineLegacy(interp, argc, argv, cmdList, redirectList) == JIM_OK) {
+ /* OK, try to exec */
+ rc = JimExecPipeline(interp, cmdList, redirectList, pidArrayPtr, outPipePtr, errFilePtr);
+ }
+ Jim_DecrRefCount(interp, cmdList);
+ Jim_DecrRefCount(interp, redirectList);
+ return rc;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* JimCleanupChildren --
*
* This is a utility procedure used to wait for child processes