Kernel: add basic support for environment variables

exec functions will search files from PATH
This commit is contained in:
Bananymous 2023-06-05 21:53:37 +03:00
parent 290b81dedc
commit 55ea5c5488
5 changed files with 137 additions and 37 deletions

View File

@ -27,7 +27,7 @@ namespace Kernel
public: public:
using entry_t = Thread::entry_t; using entry_t = Thread::entry_t;
struct userspace_entry_t struct userspace_info_t
{ {
uintptr_t entry { 0 }; uintptr_t entry { 0 };
int argc { 0 }; int argc { 0 };
@ -49,7 +49,7 @@ namespace Kernel
pid_t pid() const { return m_pid; } pid_t pid() const { return m_pid; }
static BAN::ErrorOr<LibELF::ELF*> load_elf_for_exec(BAN::StringView); static BAN::ErrorOr<LibELF::ELF*> load_elf_for_exec(BAN::StringView file_path, const BAN::String& cwd, const BAN::Vector<BAN::StringView>& path_env);
BAN::ErrorOr<Process*> fork(uintptr_t rsp, uintptr_t rip); BAN::ErrorOr<Process*> fork(uintptr_t rsp, uintptr_t rip);
BAN::ErrorOr<void> exec(BAN::StringView path, const char* const* argv, const char* const* envp); BAN::ErrorOr<void> exec(BAN::StringView path, const char* const* argv, const char* const* envp);
@ -87,7 +87,7 @@ namespace Kernel
PageTable& page_table() { return m_page_table ? *m_page_table : PageTable::kernel(); } PageTable& page_table() { return m_page_table ? *m_page_table : PageTable::kernel(); }
const userspace_entry_t& userspace_entry() const { return m_userspace_entry; } const userspace_info_t& userspace_info() const { return m_userspace_info; }
private: private:
Process(pid_t); Process(pid_t);
@ -132,7 +132,7 @@ namespace Kernel
BAN::Vector<BAN::UniqPtr<FixedWidthAllocator>> m_fixed_width_allocators; BAN::Vector<BAN::UniqPtr<FixedWidthAllocator>> m_fixed_width_allocators;
BAN::UniqPtr<GeneralAllocator> m_general_allocator; BAN::UniqPtr<GeneralAllocator> m_general_allocator;
userspace_entry_t m_userspace_entry; userspace_info_t m_userspace_info;
ExitStatus m_exit_status; ExitStatus m_exit_status;
BAN::UniqPtr<PageTable> m_page_table; BAN::UniqPtr<PageTable> m_page_table;

View File

@ -46,7 +46,7 @@ namespace Kernel
BAN::ErrorOr<Process*> Process::create_userspace(BAN::StringView path) BAN::ErrorOr<Process*> Process::create_userspace(BAN::StringView path)
{ {
auto* elf = TRY(load_elf_for_exec(path)); auto* elf = TRY(load_elf_for_exec(path, "/"sv, {}));
auto* process = create_process(); auto* process = create_process();
MUST(process->m_working_directory.push_back('/')); MUST(process->m_working_directory.push_back('/'));
@ -55,19 +55,28 @@ namespace Kernel
process->load_elf(*elf); process->load_elf(*elf);
char** argv = nullptr; char** argv = nullptr;
char** envp = nullptr;
{ {
PageTableScope _(process->page_table()); PageTableScope _(process->page_table());
argv = (char**)MUST(process->allocate(sizeof(char**) * 2)); argv = (char**)MUST(process->allocate(sizeof(char**) * 2));
argv[0] = (char*)MUST(process->allocate(path.size() + 1)); argv[0] = (char*)MUST(process->allocate(path.size() + 1));
memcpy(argv[0], path.data(), path.size()); memcpy(argv[0], path.data(), path.size());
argv[0][path.size()] = '\0'; argv[0][path.size()] = '\0';
argv[1] = nullptr; argv[1] = nullptr;
BAN::StringView env1 = "PATH=/bin:/usr/bin"sv;
envp = (char**)MUST(process->allocate(sizeof(char**) * 2));
envp[0] = (char*)MUST(process->allocate(env1.size() + 1));
memcpy(envp[0], env1.data(), env1.size());
envp[0][env1.size()] = '\0';
envp[1] = nullptr;
} }
process->m_userspace_entry.argc = 1; process->m_userspace_info.argc = 1;
process->m_userspace_entry.argv = argv; process->m_userspace_info.argv = argv;
process->m_userspace_entry.envp = (char**)0x69696969; process->m_userspace_info.envp = envp;
process->m_userspace_entry.entry = elf->file_header_native().e_entry; process->m_userspace_info.entry = elf->file_header_native().e_entry;
delete elf; delete elf;
@ -155,9 +164,54 @@ namespace Kernel
return {}; return {};
} }
BAN::ErrorOr<LibELF::ELF*> Process::load_elf_for_exec(BAN::StringView path) BAN::ErrorOr<LibELF::ELF*> Process::load_elf_for_exec(BAN::StringView file_path, const BAN::String& cwd, const BAN::Vector<BAN::StringView>& path_env)
{ {
auto elf_or_error = LibELF::ELF::load_from_file(path); if (file_path.empty())
return BAN::Error::from_errno(ENOENT);
BAN::String absolute_path;
if (file_path.front() == '/')
{
// We have an absolute path
TRY(absolute_path.append(file_path));
}
else if (file_path.front() == '.' || file_path.contains('/'))
{
// We have a relative path
TRY(absolute_path.append(cwd));
TRY(absolute_path.push_back('/'));
TRY(absolute_path.append(file_path));
}
else
{
// We have neither relative or absolute path,
// search from PATH environment
for (auto path_part : path_env)
{
if (path_part.empty())
continue;
if (path_part.front() != '/')
{
TRY(absolute_path.append(cwd));
TRY(absolute_path.push_back('/'));
}
TRY(absolute_path.append(path_part));
TRY(absolute_path.push_back('/'));
TRY(absolute_path.append(file_path));
if (!VirtualFileSystem::get().file_from_absolute_path(absolute_path, true).is_error())
break;
absolute_path.clear();
}
if (absolute_path.empty())
return BAN::Error::from_errno(ENOENT);
}
auto elf_or_error = LibELF::ELF::load_from_file(absolute_path);
if (elf_or_error.is_error()) if (elf_or_error.is_error())
{ {
if (elf_or_error.error().get_error_code() == EINVAL) if (elf_or_error.error().get_error_code() == EINVAL)
@ -195,7 +249,7 @@ namespace Kernel
forked->m_open_files = m_open_files; forked->m_open_files = m_open_files;
forked->m_userspace_entry = m_userspace_entry; forked->m_userspace_info = m_userspace_info;
for (auto* mapped_range : m_mapped_ranges) for (auto* mapped_range : m_mapped_ranges)
MUST(forked->m_mapped_ranges.push_back(mapped_range->clone(forked->page_table()))); MUST(forked->m_mapped_ranges.push_back(mapped_range->clone(forked->page_table())));
@ -219,16 +273,19 @@ namespace Kernel
BAN::ErrorOr<void> Process::exec(BAN::StringView path, const char* const* argv, const char* const* envp) BAN::ErrorOr<void> Process::exec(BAN::StringView path, const char* const* argv, const char* const* envp)
{ {
BAN::Vector<BAN::String> str_argv; BAN::Vector<BAN::String> str_argv;
while (argv && *argv) for (int i = 0; argv && argv[i]; i++)
if (str_argv.emplace_back(*argv++).is_error()) TRY(str_argv.emplace_back(argv[i]));
return BAN::Error::from_errno(ENOMEM);
BAN::Vector<BAN::StringView> path_env;
BAN::Vector<BAN::String> str_envp; BAN::Vector<BAN::String> str_envp;
while (envp && *envp) for (int i = 0; envp && envp[i]; i++)
if (str_envp.emplace_back(*envp++).is_error()) {
return BAN::Error::from_errno(ENOMEM); TRY(str_envp.emplace_back(envp[i]));
if (strncmp(envp[i], "PATH=", 5) == 0)
path_env = TRY(BAN::StringView(envp[i]).substring(5).split(':'));
}
auto* elf = TRY(load_elf_for_exec(path)); auto* elf = TRY(load_elf_for_exec(path, TRY(working_directory()), path_env));
LockGuard lock_guard(m_lock); LockGuard lock_guard(m_lock);
@ -243,7 +300,7 @@ namespace Kernel
load_elf(*elf); load_elf(*elf);
m_userspace_entry.entry = elf->file_header_native().e_entry; m_userspace_info.entry = elf->file_header_native().e_entry;
delete elf; delete elf;
@ -252,17 +309,27 @@ namespace Kernel
{ {
PageTableScope _(page_table()); PageTableScope _(page_table());
m_userspace_entry.argv = (char**)MUST(allocate(sizeof(char**) * (str_argv.size() + 1)));
m_userspace_info.argv = (char**)MUST(allocate(sizeof(char**) * (str_argv.size() + 1)));
for (size_t i = 0; i < str_argv.size(); i++) for (size_t i = 0; i < str_argv.size(); i++)
{ {
m_userspace_entry.argv[i] = (char*)MUST(allocate(str_argv[i].size() + 1)); m_userspace_info.argv[i] = (char*)MUST(allocate(str_argv[i].size() + 1));
memcpy(m_userspace_entry.argv[i], str_argv[i].data(), str_argv[i].size()); memcpy(m_userspace_info.argv[i], str_argv[i].data(), str_argv[i].size());
m_userspace_entry.argv[i][str_argv[i].size()] = '\0'; m_userspace_info.argv[i][str_argv[i].size()] = '\0';
} }
m_userspace_entry.argv[str_argv.size()] = nullptr; m_userspace_info.argv[str_argv.size()] = nullptr;
m_userspace_info.envp = (char**)MUST(allocate(sizeof(char**) * (str_envp.size() + 1)));
for (size_t i = 0; i < str_envp.size(); i++)
{
m_userspace_info.envp[i] = (char*)MUST(allocate(str_envp[i].size() + 1));
memcpy(m_userspace_info.envp[i], str_envp[i].data(), str_envp[i].size());
m_userspace_info.envp[i][str_envp[i].size()] = '\0';
}
m_userspace_info.envp[str_envp.size()] = nullptr;
} }
m_userspace_entry.argc = str_argv.size(); m_userspace_info.argc = str_argv.size();
// NOTE: These must be manually cleared since this function won't return after this point // NOTE: These must be manually cleared since this function won't return after this point
str_argv.clear(); str_argv.clear();

View File

@ -130,8 +130,8 @@ namespace Kernel
static entry_t entry_trampoline( static entry_t entry_trampoline(
[](void*) [](void*)
{ {
const auto& entry = Process::current().userspace_entry(); const auto& info = Process::current().userspace_info();
thread_userspace_trampoline(Thread::current().rsp(), entry.entry, entry.argc, entry.argv, entry.envp); thread_userspace_trampoline(Thread::current().rsp(), info.entry, info.argc, info.argv, info.envp);
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
); );

View File

@ -206,9 +206,49 @@ int execl(const char* pathname, const char* arg0, ...)
return execv(pathname, argv); return execv(pathname, argv);
} }
int execle(const char* pathname, const char* arg0, ...)
{
va_list ap;
int argc = 0;
if (arg0)
{
va_start(ap, arg0);
argc = 1;
while (va_arg(ap, const char*))
argc++;
va_end(ap);
}
char** argv = (char**)malloc(sizeof(char*) * (argc + 1));
if (argv == nullptr)
{
errno = ENOMEM;
return -1;
}
char** envp = nullptr;
va_start(ap, arg0);
argv[0] = (char*)arg0;
for (int i = 1; i < argc; i++)
argv[i] = va_arg(ap, char*);
argv[argc] = nullptr;
envp = va_arg(ap, char**);
va_end(ap);
return execve(pathname, argv, envp);
}
int execv(const char* pathname, char* const argv[]) int execv(const char* pathname, char* const argv[])
{ {
return syscall(SYS_EXEC, pathname, argv, nullptr); return execve(pathname, argv, environ);
}
int execve(const char* pathname, char* const argv[], char* const envp[])
{
return syscall(SYS_EXEC, pathname, argv, envp);
} }
pid_t fork(void) pid_t fork(void)

View File

@ -43,13 +43,6 @@ int execute_command(BAN::StringView command)
} }
else else
{ {
struct stat stat_buf;
if (stat(args.front(), &stat_buf) == -1)
{
fprintf(stderr, "command not found: %s\n", args.front());
return 1;
}
pid_t pid = fork(); pid_t pid = fork();
if (pid == 0) if (pid == 0)
{ {
@ -60,7 +53,7 @@ int execute_command(BAN::StringView command)
} }
if (pid == -1) if (pid == -1)
{ {
perror("fork()"); perror("fork");
return 1; return 1;
} }
int status; int status;