LibC: Add support for shebangs
I implemented shebangs in userspace as I don't really see the benefit of doing it in kernel space. Only benefit I can think of is executing non readable scripts but I don't really see the benefit in that.
This commit is contained in:
parent
ab4dd6a268
commit
2ff3f88b4d
|
@ -1,9 +1,11 @@
|
||||||
#include <BAN/Assert.h>
|
#include <BAN/Assert.h>
|
||||||
#include <BAN/Debug.h>
|
#include <BAN/Debug.h>
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
|
||||||
#include <kernel/Memory/Types.h>
|
#include <kernel/Memory/Types.h>
|
||||||
#include <kernel/Syscall.h>
|
#include <kernel/Syscall.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
@ -165,8 +167,16 @@ int gethostname(char* name, size_t namelen)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int exec_impl(const char* pathname, char* const* argv, char* const* envp, bool do_path_resolution)
|
static int exec_impl_shebang(FILE* fp, const char* pathname, char* const* argv, char* const* envp, int shebang_depth);
|
||||||
|
|
||||||
|
static int exec_impl(const char* pathname, char* const* argv, char* const* envp, bool do_path_resolution, int shebang_depth = 0)
|
||||||
{
|
{
|
||||||
|
if (shebang_depth > 100)
|
||||||
|
{
|
||||||
|
errno = ELOOP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
char buffer[PATH_MAX];
|
char buffer[PATH_MAX];
|
||||||
|
|
||||||
if (do_path_resolution && strchr(pathname, '/') == nullptr)
|
if (do_path_resolution && strchr(pathname, '/') == nullptr)
|
||||||
|
@ -211,9 +221,101 @@ static int exec_impl(const char* pathname, char* const* argv, char* const* envp,
|
||||||
pathname = resolved;
|
pathname = resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (access(pathname, X_OK) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (FILE* fp = fopen(pathname, "r"))
|
||||||
|
{
|
||||||
|
char shebang[2];
|
||||||
|
if (fread(shebang, 1, 2, fp) == 2 && shebang[0] == '#' && shebang[1] == '!')
|
||||||
|
return exec_impl_shebang(fp, pathname, argv, envp, shebang_depth);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
return syscall(SYS_EXEC, pathname, argv, envp);
|
return syscall(SYS_EXEC, pathname, argv, envp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int exec_impl_shebang(FILE* fp, const char* pathname, char* const* argv, char* const* envp, int shebang_depth)
|
||||||
|
{
|
||||||
|
constexpr size_t buffer_len = PATH_MAX + 1 + ARG_MAX + 1;
|
||||||
|
char* buffer = static_cast<char*>(malloc(buffer_len));
|
||||||
|
if (buffer == nullptr)
|
||||||
|
{
|
||||||
|
fclose(fp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fgets(buffer, buffer_len, fp) == nullptr)
|
||||||
|
{
|
||||||
|
free(buffer);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto sv_trim_whitespace =
|
||||||
|
[](BAN::StringView sv) -> BAN::StringView
|
||||||
|
{
|
||||||
|
while (!sv.empty() && isspace(sv.front()))
|
||||||
|
sv = sv.substring(1);
|
||||||
|
while (!sv.empty() && isspace(sv.back()))
|
||||||
|
sv = sv.substring(0, sv.size() - 1);
|
||||||
|
return sv;
|
||||||
|
};
|
||||||
|
|
||||||
|
BAN::StringView buffer_sv = buffer;
|
||||||
|
if (buffer_sv.back() != '\n')
|
||||||
|
{
|
||||||
|
free(buffer);
|
||||||
|
errno = ENOEXEC;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
buffer_sv = sv_trim_whitespace(buffer_sv);
|
||||||
|
|
||||||
|
BAN::StringView interpreter, argument;
|
||||||
|
if (auto space = buffer_sv.find([](char ch) -> bool { return isspace(ch); }); !space.has_value())
|
||||||
|
interpreter = buffer_sv;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
interpreter = sv_trim_whitespace(buffer_sv.substring(0, space.value()));
|
||||||
|
argument = sv_trim_whitespace(buffer_sv.substring(space.value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interpreter.empty())
|
||||||
|
{
|
||||||
|
free(buffer);
|
||||||
|
errno = ENOEXEC;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// null terminate interpreter and argument
|
||||||
|
const_cast<char*>(interpreter.data())[interpreter.size()] = '\0';
|
||||||
|
if (!argument.empty())
|
||||||
|
const_cast<char*>(argument.data())[argument.size()] = '\0';
|
||||||
|
|
||||||
|
size_t old_argc = 0;
|
||||||
|
while (argv[old_argc])
|
||||||
|
old_argc++;
|
||||||
|
|
||||||
|
const size_t extra_args = 1 + !argument.empty();
|
||||||
|
char** new_argv = static_cast<char**>(malloc((extra_args + old_argc + 1) * sizeof(char*)));;
|
||||||
|
if (new_argv == nullptr)
|
||||||
|
{
|
||||||
|
free(buffer);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_argv[0] = const_cast<char*>(pathname);
|
||||||
|
if (!argument.empty())
|
||||||
|
new_argv[1] = const_cast<char*>(argument.data());
|
||||||
|
for (size_t i = 0; i < old_argc; i++)
|
||||||
|
new_argv[i + extra_args] = argv[i];
|
||||||
|
new_argv[old_argc + extra_args] = nullptr;
|
||||||
|
|
||||||
|
exec_impl(interpreter.data(), new_argv, envp, true, shebang_depth + 1);
|
||||||
|
free(new_argv);
|
||||||
|
free(buffer);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int execl_impl(const char* pathname, const char* arg0, va_list ap, bool has_env, bool do_path_resolution)
|
static int execl_impl(const char* pathname, const char* arg0, va_list ap, bool has_env, bool do_path_resolution)
|
||||||
{
|
{
|
||||||
int argc = 1;
|
int argc = 1;
|
||||||
|
|
Loading…
Reference in New Issue