123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828 |
- /*++
- Copyright (c) 2013 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- exec.c
- Abstract:
- This module implements the exec() family of functions.
- Author:
- Evan Green 3-Apr-2013
- Environment:
- User Mode C Library
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include "libcp.h"
- #include <assert.h>
- #include <ctype.h>
- #include <errno.h>
- #include <limits.h>
- #include <stdarg.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // ------------------------------------------------------------------ Functions
- //
- LIBC_API
- int
- execl (
- const char *Path,
- const char *Argument0,
- ...
- )
- /*++
- Routine Description:
- This routine replaces the current process image with a new image.
- Arguments:
- Path - Supplies a pointer to a string containing the fully specified path
- of the file to execute.
- Argument0 - Supplies the first argument to execute, usually the same as
- the command name.
- ... - Supplies the arguments to the program. The argument list must be
- terminated with a NULL.
- Return Value:
- Does not return on success, the current process is gutted and replaced with
- the specified image.
- -1 on error, and the errno variable will be set to contain more information.
- --*/
- {
- char *Argument;
- char **ArgumentArray;
- ULONG ArgumentCount;
- ULONG ArgumentIndex;
- va_list ArgumentList;
- int Result;
- //
- // Determine the number of arguments.
- //
- ArgumentCount = 1;
- va_start(ArgumentList, Argument0);
- Argument = va_arg(ArgumentList, char *);
- while (Argument != NULL) {
- ArgumentCount += 1;
- Argument = va_arg(ArgumentList, char *);
- }
- va_end(ArgumentList);
- //
- // Create an array for them.
- //
- ArgumentArray = malloc((ArgumentCount + 1) * sizeof(char *));
- if (ArgumentArray == NULL) {
- errno = ENOMEM;
- return -1;
- }
- //
- // Copy the arguments into the array.
- //
- ArgumentIndex = 1;
- ArgumentArray[0] = (char *)Argument0;
- va_start(ArgumentList, Argument0);
- Argument = va_arg(ArgumentList, char *);
- while (Argument != NULL) {
- ArgumentArray[ArgumentIndex] = Argument;
- ArgumentIndex += 1;
- Argument = va_arg(ArgumentList, char *);
- }
- ArgumentArray[ArgumentIndex] = NULL;
- //
- // The environment is one more along.
- //
- Result = execve(Path,
- (char *const *)ArgumentArray,
- (char *const *)environ);
- free(ArgumentArray);
- return Result;
- }
- LIBC_API
- int
- execv (
- const char *Path,
- char *const Arguments[]
- )
- /*++
- Routine Description:
- This routine replaces the current process image with a new image.
- Arguments:
- Path - Supplies a pointer to a string containing the fully specified path
- of the file to execute.
- Arguments - Supplies an array of pointers to strings containing the
- arguments to pass to the program.
- Return Value:
- Does not return on success, the current process is gutted and replaced with
- the specified image.
- -1 on error, and the errno variable will be set to contain more information.
- --*/
- {
- return execve(Path, Arguments, (char *const *)environ);
- }
- LIBC_API
- int
- execle (
- const char *Path,
- const char *Argument0,
- ...
- )
- /*++
- Routine Description:
- This routine replaces the current process image with a new image. The
- parameters to this function also include the environment variables to use.
- Arguments:
- Path - Supplies a pointer to a string containing the fully specified path
- of the file to execute.
- Argument0 - Supplies the first argument to the program, usually the same
- as the program name.
- ... - Supplies the arguments to the program. The argument list must be
- terminated with a NULL. After the NULL an array of strings representing
- the environment is expected (think of it like a final argument after
- the NULL in the form const char *envp[]).
- Return Value:
- Does not return on success, the current process is gutted and replaced with
- the specified image.
- -1 on error, and the errno variable will be set to contain more information.
- --*/
- {
- char *Argument;
- char **ArgumentArray;
- ULONG ArgumentCount;
- ULONG ArgumentIndex;
- va_list ArgumentList;
- char *const *Environment;
- int Result;
- //
- // Determine the number of arguments.
- //
- ArgumentCount = 1;
- va_start(ArgumentList, Argument0);
- Argument = va_arg(ArgumentList, char *);
- while (Argument != NULL) {
- ArgumentCount += 1;
- Argument = va_arg(ArgumentList, char *);
- }
- va_end(ArgumentList);
- //
- // Create an array for them.
- //
- ArgumentArray = malloc((ArgumentCount + 1) * sizeof(char *));
- if (ArgumentArray == NULL) {
- errno = ENOMEM;
- return -1;
- }
- //
- // Copy the arguments into the array.
- //
- ArgumentIndex = 1;
- ArgumentArray[0] = (char *)Argument0;
- va_start(ArgumentList, Argument0);
- Argument = va_arg(ArgumentList, char *);
- while (Argument != NULL) {
- ArgumentArray[ArgumentIndex] = Argument;
- ArgumentIndex += 1;
- Argument = va_arg(ArgumentList, char *);
- }
- ArgumentArray[ArgumentIndex] = NULL;
- //
- // The environment is one more along.
- //
- Environment = va_arg(ArgumentList, char *const *);
- Result = execve(Path, (char *const *)ArgumentArray, Environment);
- free(ArgumentArray);
- return Result;
- }
- LIBC_API
- int
- execve (
- const char *Path,
- char *const Arguments[],
- char *const Environment[]
- )
- /*++
- Routine Description:
- This routine replaces the current process image with a new image. The
- parameters to this function also include the environment variables to use.
- Arguments:
- Path - Supplies a pointer to a string containing the fully specified path
- of the file to execute.
- Arguments - Supplies an array of pointers to strings containing the
- arguments to pass to the program.
- Environment - Supplies an array of pointers to strings containing the
- environment variables to pass to the program.
- Return Value:
- Does not return on success, the current process is gutted and replaced with
- the specified image.
- -1 on error, and the errno variable will be set to contain more information.
- --*/
- {
- UINTN ArgumentCount;
- UINTN ArgumentIndex;
- UINTN ArgumentValuesTotalLength;
- PSTR End;
- UINTN EnvironmentCount;
- UINTN EnvironmentValuesTotalLength;
- UINTN ExtraArguments;
- PSTR Interpreter;
- PSTR InterpreterArgument;
- UINTN InterpreterArgumentLength;
- UINTN InterpreterLength;
- PSTR Line;
- UINTN PathLength;
- PPROCESS_ENVIRONMENT ProcessEnvironment;
- FILE *Script;
- char **ShellArguments;
- KSTATUS Status;
- fflush(NULL);
- ArgumentCount = 0;
- ArgumentValuesTotalLength = 0;
- EnvironmentCount = 0;
- EnvironmentValuesTotalLength = 0;
- Script = NULL;
- ShellArguments = NULL;
- Line = NULL;
- PathLength = strlen(Path) + 1;
- while (Arguments[ArgumentCount] != NULL) {
- ArgumentValuesTotalLength += strlen(Arguments[ArgumentCount]) + 1;
- ArgumentCount += 1;
- }
- if (Environment != NULL) {
- while (Environment[EnvironmentCount] != NULL) {
- EnvironmentValuesTotalLength +=
- strlen(Environment[EnvironmentCount]) + 1;
- EnvironmentCount += 1;
- }
- }
- ProcessEnvironment = OsCreateEnvironment((PSTR)Path,
- PathLength,
- (PSTR *)Arguments,
- ArgumentValuesTotalLength,
- ArgumentCount,
- (PSTR *)Environment,
- EnvironmentValuesTotalLength,
- EnvironmentCount);
- if (ProcessEnvironment == NULL) {
- errno = ENOMEM;
- goto execveEnd;
- }
- Status = OsExecuteImage(ProcessEnvironment);
- if ((!KSUCCESS(Status)) && (Status != STATUS_UNKNOWN_IMAGE_FORMAT)) {
- errno = ClConvertKstatusToErrorNumber(Status);
- goto execveEnd;
- }
- //
- // Take a peek at the file. If it begins with #!, then interpret it as a
- // shell script.
- //
- Script = fopen(Path, "r");
- if (Script == NULL) {
- goto execveEnd;
- }
- Line = malloc(PATH_MAX + 6);
- if (Line == NULL) {
- errno = ENOMEM;
- goto execveEnd;
- }
- if (fgets(Line, PATH_MAX + 6, Script) == NULL) {
- goto execveEnd;
- }
- if ((Line[0] != '#') || (Line[1] != '!')) {
- errno = ENOEXEC;
- goto execveEnd;
- }
- Interpreter = Line + 2;
- while (isblank(*Interpreter) != 0) {
- Interpreter += 1;
- }
- if (*Interpreter == '\0') {
- errno = ENOEXEC;
- goto execveEnd;
- }
- //
- // Find the first space, newline, or ending and terminate the interpreter
- // path.
- //
- End = Interpreter;
- while ((*End != '\0') && (!isspace(*End))) {
- End += 1;
- }
- ExtraArguments = 1;
- InterpreterArgument = NULL;
- InterpreterArgumentLength = 0;
- //
- // If there's more to the line, then there's an argument. Treat the
- // remainder of the line as one big argument.
- //
- if ((*End != '\0') && (*End != '\n') && (*End != '\r')) {
- ExtraArguments += 1;
- *End = '\0';
- End += 1;
- while (isblank(*End)) {
- End += 1;
- }
- if ((*End != '\0') && (*End != '\n') && (*End != '\r')) {
- InterpreterArgument = End;
- while ((*End != '\r') && (*End != '\n') && (*End != '\0')) {
- End += 1;
- }
- InterpreterArgumentLength =
- (UINTN)End - (UINTN)InterpreterArgument + 1;
- *End = '\0';
- }
- //
- // No argument, just terminate the interpreter.
- //
- } else {
- *End = '\0';
- }
- //
- // Perform some basic (but not nearly foolproof) infinite loop detection
- // by looking to see if the interpreter is the same as the file itself.
- //
- if (strcmp(Interpreter, Path) == 0) {
- fprintf(stderr, "Error: Infinite exec loop for '%s'.\n", Path);
- errno = ENOEXEC;
- goto execveEnd;
- }
- if (access(Interpreter, X_OK) != 0) {
- goto execveEnd;
- }
- //
- // Create the arguments for a shell interpreter.
- //
- OsDestroyEnvironment(ProcessEnvironment);
- ProcessEnvironment = NULL;
- ShellArguments = malloc(
- sizeof(char *) * (ArgumentCount + ExtraArguments + 1));
- if (ShellArguments == NULL) {
- errno = ENOMEM;
- return -1;
- }
- InterpreterLength = strlen(Interpreter) + 1;
- ShellArguments[0] = Interpreter;
- ArgumentValuesTotalLength += InterpreterLength;
- if (ArgumentCount == 0) {
- ArgumentCount = 1;
- } else {
- ArgumentValuesTotalLength -= strlen(Arguments[0]) + 1;
- }
- if (InterpreterArgument != NULL) {
- ShellArguments[1] = InterpreterArgument;
- ArgumentValuesTotalLength += InterpreterArgumentLength;
- }
- ShellArguments[ExtraArguments] = (char *)Path;
- ArgumentValuesTotalLength += PathLength;
- for (ArgumentIndex = 1; ArgumentIndex < ArgumentCount; ArgumentIndex += 1) {
- ShellArguments[ArgumentIndex + ExtraArguments] =
- Arguments[ArgumentIndex];
- }
- ShellArguments[ArgumentIndex + ExtraArguments] = NULL;
- //
- // Add the extras for the interpreter and possibly its parameter.
- //
- ArgumentCount += ExtraArguments;
- //
- // Create the environment and try to execute this puppy. The interpreter
- // line cannot itself point to a script, that would be downright silly.
- //
- ProcessEnvironment = OsCreateEnvironment(Interpreter,
- InterpreterLength,
- (PSTR *)ShellArguments,
- ArgumentValuesTotalLength,
- ArgumentCount,
- (PSTR *)Environment,
- EnvironmentValuesTotalLength,
- EnvironmentCount);
- if (ProcessEnvironment == NULL) {
- errno = ENOMEM;
- goto execveEnd;
- }
- Status = OsExecuteImage(ProcessEnvironment);
- errno = ClConvertKstatusToErrorNumber(Status);
- execveEnd:
- if (ProcessEnvironment != NULL) {
- OsDestroyEnvironment(ProcessEnvironment);
- }
- if (Script != NULL) {
- fclose(Script);
- }
- if (Line != NULL) {
- free(Line);
- }
- if (ShellArguments != NULL) {
- free(ShellArguments);
- }
- return -1;
- }
- LIBC_API
- int
- execlp (
- const char *File,
- const char *Argument0,
- ...
- )
- /*++
- Routine Description:
- This routine replaces the current process image with a new image. If the
- given file is found but of an unrecognized binary format, then a shell
- interpreter will be launched and passed the file.
- Arguments:
- File - Supplies a pointer to a string containing the name of the executable,
- which will be searched for on the PATH if the string does not contain a
- slash.
- Argument0 - Supplies the first argument to the program. Additional arguments
- follow in the ellipses. The argument list must be terminated with a
- NULL.
- ... - Supplies any remaining arguments.
- Return Value:
- Does not return on success, the current process is gutted and replaced with
- the specified image.
- -1 on error, and the errno variable will be set to contain more information.
- --*/
- {
- char *Argument;
- char **ArgumentArray;
- ULONG ArgumentCount;
- ULONG ArgumentIndex;
- va_list ArgumentList;
- int Result;
- //
- // Determine the number of arguments.
- //
- ArgumentCount = 1;
- va_start(ArgumentList, Argument0);
- Argument = va_arg(ArgumentList, char *);
- while (Argument != NULL) {
- ArgumentCount += 1;
- Argument = va_arg(ArgumentList, char *);
- }
- va_end(ArgumentList);
- //
- // Create an array for them.
- //
- ArgumentArray = malloc((ArgumentCount + 1) * sizeof(char *));
- if (ArgumentArray == NULL) {
- errno = ENOMEM;
- return -1;
- }
- //
- // Copy the arguments into the array.
- //
- ArgumentIndex = 1;
- ArgumentArray[0] = (char *)Argument0;
- va_start(ArgumentList, Argument0);
- Argument = va_arg(ArgumentList, char *);
- while (Argument != NULL) {
- ArgumentArray[ArgumentIndex] = Argument;
- ArgumentIndex += 1;
- Argument = va_arg(ArgumentList, char *);
- }
- ArgumentArray[ArgumentIndex] = NULL;
- Result = execvpe(File, (char *const *)ArgumentArray, environ);
- free(ArgumentArray);
- return Result;
- }
- LIBC_API
- int
- execvp (
- const char *File,
- char *const Arguments[]
- )
- /*++
- Routine Description:
- This routine replaces the current process image with a new image. If the
- given file is found but of an unrecognized binary format, then a shell
- interpreter will be launched and passed the file.
- Arguments:
- File - Supplies a pointer to a string containing the name of the executable,
- which will be searched for on the PATH if the string does not contain a
- slash.
- Arguments - Supplies an array of pointers to strings containing the
- arguments to pass to the program.
- Return Value:
- Does not return on success, the current process is gutted and replaced with
- the specified image.
- -1 on error, and the errno variable will be set to contain more information.
- --*/
- {
- return execvpe(File, Arguments, (char *const *)environ);
- }
- LIBC_API
- int
- execvpe (
- const char *File,
- char *const Arguments[],
- char *const Environment[]
- )
- /*++
- Routine Description:
- This routine replaces the current process image with a new image. The
- parameters to this function also include the environment variables to use.
- If the given file is found but of an unrecognized binary format, then a
- shell interpreter will be launched and passed the file.
- Arguments:
- File - Supplies a pointer to a string containing the name of the executable,
- which will be searched for on the PATH if the string does not contain a
- slash.
- Arguments - Supplies an array of pointers to strings containing the
- arguments to pass to the program.
- Environment - Supplies an array of pointers to strings containing the
- environment variables to pass to the program.
- Return Value:
- Does not return on success, the current process is gutted and replaced with
- the specified image.
- -1 on error, and the errno variable will be set to contain more information.
- --*/
- {
- PSTR CombinedPath;
- size_t FileLength;
- int OriginalError;
- PSTR PathCopy;
- PSTR PathEntry;
- size_t PathEntryLength;
- PSTR PathVariable;
- char *Token;
- PathVariable = getenv("PATH");
- //
- // If the path contains a slash, use it directly without searching the
- // PATH variable.
- //
- if ((strchr(File, '/') != NULL) || (PathVariable == NULL) ||
- (*PathVariable == '\0')) {
- return execve(File, Arguments, Environment);
- }
- //
- // The path doesn't have a slash and there's a path variable, so try
- // searching the path.
- //
- PathCopy = strdup(PathVariable);
- if (PathCopy == NULL) {
- errno = ENOMEM;
- return -1;
- }
- FileLength = strlen(File);
- PathEntry = strtok_r(PathCopy, ":", &Token);
- while (PathEntry != NULL) {
- PathEntryLength = strlen(PathEntry);
- if (PathEntryLength == 0) {
- PathEntry = ".";
- PathEntryLength = 1;
- }
- if (PathEntry[PathEntryLength - 1] == '/') {
- PathEntryLength -= 1;
- }
- CombinedPath = malloc(PathEntryLength + FileLength + 2);
- if (CombinedPath == NULL) {
- errno = ENOMEM;
- break;
- }
- memcpy(CombinedPath, PathEntry, PathEntryLength);
- CombinedPath[PathEntryLength] = '/';
- strcpy(CombinedPath + PathEntryLength + 1, File);
- //
- // Recurse, though this won't recurse further because now there's a
- // slash in the path.
- //
- OriginalError = errno;
- if (access(CombinedPath, X_OK) == 0) {
- execvpe(CombinedPath, Arguments, Environment);
- } else {
- errno = OriginalError;
- }
- free(CombinedPath);
- //
- // Move to the next path entry.
- //
- PathEntry = strtok_r(NULL, ":", &Token);
- }
- if (errno == 0) {
- errno = ENOENT;
- }
- free(PathCopy);
- return -1;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
|