diff --git a/headers/private/libroot/libroot_private.h b/headers/private/libroot/libroot_private.h index b510d1ea94..9fe425a794 100644 --- a/headers/private/libroot/libroot_private.h +++ b/headers/private/libroot/libroot_private.h @@ -12,7 +12,9 @@ struct uspace_program_args; struct real_time_data; -status_t __test_executable(const char *path, char *starter); +status_t __parse_invoke_line(char *invoker, char ***_newArgs, + char * const **_oldArgs, int32 *_argCount); +status_t __test_executable(const char *path, char *invoker); void __init_image(const struct uspace_program_args *args); void __init_dlfcn(const struct uspace_program_args *args); void __init_env(const struct uspace_program_args *args); diff --git a/src/system/libroot/os/image.c b/src/system/libroot/os/image.c index 47c9efdd0f..13ad1b6168 100644 --- a/src/system/libroot/os/image.c +++ b/src/system/libroot/os/image.c @@ -20,8 +20,8 @@ static struct rld_export const *sRuntimeLinker; thread_id load_image(int32 argCount, const char **args, const char **environ) { - char starter[B_FILE_NAME_LENGTH]; - const char **newArgs = NULL; + char invoker[B_FILE_NAME_LENGTH]; + char **newArgs = NULL; int32 envCount = 0; thread_id thread; @@ -30,26 +30,15 @@ load_image(int32 argCount, const char **args, const char **environ) // test validity of executable + support for scripts { - status_t status = __test_executable(args[0], starter); + status_t status = __test_executable(args[0], invoker); if (status < B_OK) return status; - if (starter[0]) { - int32 i; - - // this is a shell script and requires special treatment - newArgs = malloc((argCount + 2) * sizeof(void *)); - if (newArgs == NULL) - return B_NO_MEMORY; - - // copy args and have "starter" as new app - newArgs[0] = starter; - for (i = 0; i < argCount; i++) - newArgs[i + 1] = args[i]; - newArgs[i + 1] = NULL; - - args = newArgs; - argCount++; + if (invoker[0]) { + status = __parse_invoke_line(invoker, &newArgs, + (char * const **)&args, &argCount); + if (status < B_OK) + return status; } } @@ -118,10 +107,84 @@ clear_caches(void *address, size_t length, uint32 flags) // #pragma mark - -status_t -__test_executable(const char *path, char *starter) +static char * +next_argument(char **_start, bool separate) { - return sRuntimeLinker->test_executable(path, geteuid(), getegid(), starter); + char *line = *_start; + char quote = 0; + int32 i; + + // eliminate leading spaces + while (line[0] == ' ') + line++; + + if (line[0] == '"' || line[0] == '\'') { + quote = line[0]; + line++; + } + + if (!line[0]) + return NULL; + + for (i = 0;; i++) { + if (line[i] == '\\' && line[i + 1] != '\0') + continue; + + if ((!quote && line[i] == ' ') || line[i] == quote || line[i] == '\0') { + // argument separator! + if (separate) + line[i] = '\0'; + *_start = &line[i + 1]; + return line; + } + } + + return NULL; +} + + +status_t +__parse_invoke_line(char *invoker, char ***_newArgs, + char * const **_oldArgs, int32 *_argCount) +{ + int32 i, count = 0; + char *arg = invoker; + char **newArgs; + + // count arguments in the line + + while (next_argument(&arg, false)) { + count++; + } + + // this is a shell script and requires special treatment + newArgs = malloc((*_argCount + count + 1) * sizeof(void *)); + if (newArgs == NULL) + return B_NO_MEMORY; + + // copy invoker and old arguments and to newArgs + + for (i = 0; (arg = next_argument(&invoker, true)) != NULL; i++) { + newArgs[i] = arg; + } + for (i = 0; i < *_argCount; i++) { + newArgs[i + count] = (char *)(*_oldArgs)[i]; + } + + newArgs[i + count] = NULL; + + *_newArgs = newArgs; + *_oldArgs = (char * const *)newArgs; + *_argCount += count; + + return B_OK; +} + + +status_t +__test_executable(const char *path, char *invoker) +{ + return sRuntimeLinker->test_executable(path, geteuid(), getegid(), invoker); } diff --git a/src/system/libroot/posix/unistd/exec.c b/src/system/libroot/posix/unistd/exec.c index ec1a70bfa9..38f643dbdc 100644 --- a/src/system/libroot/posix/unistd/exec.c +++ b/src/system/libroot/posix/unistd/exec.c @@ -54,7 +54,7 @@ int execve(const char *path, char * const args[], char * const environment[]) { int32 argCount = 0, envCount = 0; - char starter[B_FILE_NAME_LENGTH]; + char invoker[B_FILE_NAME_LENGTH]; char **newArgs = NULL; // count argument/environment list entries here, we don't want @@ -72,31 +72,20 @@ execve(const char *path, char * const args[], char * const environment[]) // test validity of executable + support for scripts { - status_t status = __test_executable(args[0], starter); + status_t status = __test_executable(args[0], invoker); if (status < B_OK) { errno = status; return -1; } - if (starter[0]) { - int32 i; - - // this is a shell script and requires special treatment - newArgs = malloc((argCount + 2) * sizeof(void *)); - if (newArgs == NULL) { - errno = B_NO_MEMORY; + if (invoker[0]) { + status = __parse_invoke_line(invoker, &newArgs, &args, &argCount); + if (status < B_OK) { + errno = status; return -1; } - // copy args and have "starter" as new app - newArgs[0] = starter; - for (i = 0; i < argCount; i++) - newArgs[i + 1] = args[i]; - newArgs[i + 1] = NULL; - - path = starter; - args = newArgs; - argCount++; + path = newArgs[0]; } } diff --git a/src/system/runtime_loader/rld.c b/src/system/runtime_loader/rld.c index edc5351048..c68f5ce2eb 100644 --- a/src/system/runtime_loader/rld.c +++ b/src/system/runtime_loader/rld.c @@ -206,7 +206,7 @@ open_executable(char *name, image_type type, const char *rpath) */ status_t -test_executable(const char *name, uid_t user, gid_t group, char *starter) +test_executable(const char *name, uid_t user, gid_t group, char *invoker) { char path[B_PATH_NAME_LENGTH]; char buffer[B_FILE_NAME_LENGTH]; @@ -251,18 +251,23 @@ test_executable(const char *name, uid_t user, gid_t group, char *starter) if (status == B_NOT_AN_EXECUTABLE) { // test for shell scripts if (!strncmp(buffer, "#!", 2)) { - status = B_OK; - if (starter) { - char *end; - buffer[length - 1] = '\0'; + char *end; + buffer[length - 1] = '\0'; - if ((end = strchr(buffer, '\n')) != NULL) - end[0] = '\0'; - strcpy(starter, buffer + 2); - } + end = strchr(buffer, '\n'); + if (end == NULL) { + status = E2BIG; + goto out; + } else + end[0] = '\0'; + + if (invoker) + strcpy(invoker, buffer + 2); + + status = B_OK; } - } else if (status == B_OK && starter) - starter[0] = '\0'; + } else if (status == B_OK && invoker) + invoker[0] = '\0'; out: _kern_close(fd);