Compare commits

..

5 Commits

Author SHA1 Message Date
9bb1720cca
libc: Add execvp() and execvpe()
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-07 15:39:10 +02:00
fe348d56c0
String+StringView: Add split() 2023-04-07 15:14:46 +02:00
3a28771520
kernel+libc+apps: Add support for environment variables 2023-04-07 15:03:38 +02:00
3ef484b3f3
kernel: Add missing include 2023-04-07 14:37:06 +02:00
3a70accdeb
kernel: Move copy_string_vector_to_userspace to ThreadImage 2023-04-07 14:36:24 +02:00
20 changed files with 190 additions and 71 deletions

View File

@ -8,6 +8,7 @@ endfunction()
luna_app(init.cpp init) luna_app(init.cpp init)
luna_app(sh.cpp sh) luna_app(sh.cpp sh)
luna_app(env.cpp env)
luna_app(cat.cpp cat) luna_app(cat.cpp cat)
target_link_libraries(cat PRIVATE os) target_link_libraries(cat PRIVATE os)

9
apps/env.cpp Normal file
View File

@ -0,0 +1,9 @@
#include <stdio.h>
extern char** environ;
int main()
{
char** env = environ;
while (*env) { puts(*(env++)); }
}

View File

@ -43,7 +43,8 @@ int main()
if (ret == 0) if (ret == 0)
{ {
char* argv[] = { "/bin/sh", NULL }; char* argv[] = { "/bin/sh", NULL };
execv(argv[0], argv); char* envp[] = { "PATH=/bin:/sbin", NULL };
execve(argv[0], argv, envp);
perror("execv"); perror("execv");
return 1; return 1;
} }

View File

@ -31,50 +31,6 @@ static Result<Vector<char*>> split_command_into_argv(const char* cmd)
return result; return result;
} }
static Result<String> join_path(const char* str1, const char* str2)
{
StringBuilder sb;
TRY(sb.add(StringView { str1 }));
TRY(sb.add(StringView { str2 }));
return sb.string();
}
static int execute_in_path(const char* dir, char** argv)
{
String path;
bool ok = join_path(dir, argv[0]).try_move_value_or_error(path, errno);
if (!ok) return -1;
int err = errno;
int status = execv(path.chars(), argv);
if (errno == ENOENT)
{
errno = err;
return 0;
}
return status;
}
static int sh_execvp(char** argv)
{
if (strchr(argv[0], '/'))
{
// Relative paths do not need path resolution.
return execv(argv[0], argv);
}
if (execute_in_path("/bin/", argv) != 0) return -1;
if (execute_in_path("/sbin/", argv) != 0) return -1;
if (execute_in_path("/usr/bin/", argv) != 0) return -1;
if (execute_in_path("/usr/local/bin/", argv) != 0) return -1;
errno = ENOENT;
return -1;
}
int main() int main()
{ {
while (1) while (1)
@ -105,7 +61,7 @@ int main()
} }
if (argv[0] == NULL) return 0; if (argv[0] == NULL) return 0;
sh_execvp(argv.data()); execvp(argv[0], argv.data());
perror(argv[0]); perror(argv[0]);
return 1; return 1;
} }

View File

@ -31,27 +31,13 @@ static Result<Vector<String>> copy_string_vector_from_userspace(u64 address)
return result; return result;
} }
static Result<u64> copy_string_vector_to_userspace(const Vector<String>& vec, ThreadImage& image) Result<u64> sys_execve(Registers* regs, SyscallArgs args)
{
Vector<u64> user_vec;
for (const auto& item : vec)
{
// Copy each individual string and retrieve a userspace pointer to said copy
u64 addr = TRY(image.push_mem_on_stack((const u8*)item.chars(), item.length() + 1));
TRY(user_vec.try_append(addr));
}
TRY(user_vec.try_append((u64) nullptr));
// Copy the actual vector of userspace pointers to the stack
return TRY(image.push_mem_on_stack((u8*)user_vec.data(), user_vec.size() * sizeof(u64)));
}
Result<u64> sys_exec(Registers* regs, SyscallArgs args)
{ {
auto path = TRY(MemoryManager::strdup_from_user(args[0])); auto path = TRY(MemoryManager::strdup_from_user(args[0]));
auto argv = TRY(copy_string_vector_from_userspace(args[1])); auto argv = TRY(copy_string_vector_from_userspace(args[1]));
auto envp = TRY(copy_string_vector_from_userspace(args[2]));
// FIXME: Make sure argv is not too big. // FIXME: Make sure argv & envp are not too big.
auto inode = TRY(VFS::resolve_path(path.chars())); auto inode = TRY(VFS::resolve_path(path.chars()));
@ -68,9 +54,14 @@ Result<u64> sys_exec(Registers* regs, SyscallArgs args)
kdbgln("exec: copying argv to image memory (argc = %zu)", argv.size()); kdbgln("exec: copying argv to image memory (argc = %zu)", argv.size());
u64 user_argv = TRY(copy_string_vector_to_userspace(argv, *image)); u64 user_argv = TRY(image->push_string_vector_on_stack(argv));
usize user_argc = argv.size(); usize user_argc = argv.size();
kdbgln("exec: copying envp to image memory (envc = %zu)", envp.size());
u64 user_envp = TRY(image->push_string_vector_on_stack(envp));
usize user_envc = envp.size();
// From now on, nothing should fail. // From now on, nothing should fail.
kinfoln("exec: image load ok, will now replace existing process image"); kinfoln("exec: image load ok, will now replace existing process image");
@ -92,7 +83,7 @@ Result<u64> sys_exec(Registers* regs, SyscallArgs args)
MMU::switch_page_directory(current->directory); MMU::switch_page_directory(current->directory);
current->set_arguments(user_argc, user_argv, 0, 0); current->set_arguments(user_argc, user_argv, user_envc, user_envp);
memcpy(regs, &current->regs, sizeof(*regs)); memcpy(regs, &current->regs, sizeof(*regs));

View File

@ -115,13 +115,22 @@ namespace Scheduler
thread->name = name; thread->name = name;
thread->parent_id = 0; thread->parent_id = 0;
Vector<String> args;
auto name_string = TRY(String::from_cstring(name));
TRY(args.try_append(move(name_string)));
Vector<String> env;
auto guard = make_scope_guard([&] { delete thread; }); auto guard = make_scope_guard([&] { delete thread; });
auto image = TRY(ThreadImage::try_load_from_elf(inode)); auto image = TRY(ThreadImage::try_load_from_elf(inode));
u64 argv = TRY(image->push_string_vector_on_stack(args));
u64 envp = TRY(image->push_string_vector_on_stack(env));
guard.deactivate(); guard.deactivate();
image->apply(thread); image->apply(thread);
thread->set_arguments(args.size(), argv, env.size(), envp);
kinfoln("Created userspace thread: id %lu with ip %#.16lx and sp %#.16lx (ksp %#lx)", thread->id, thread->ip(), kinfoln("Created userspace thread: id %lu with ip %#.16lx and sp %#.16lx (ksp %#lx)", thread->id, thread->ip(),
thread->sp(), thread->kernel_stack.top()); thread->sp(), thread->kernel_stack.top());

View File

@ -92,6 +92,21 @@ Result<u64> ThreadImage::push_mem_on_stack(const u8* mem, usize size)
return m_sp; return m_sp;
} }
Result<u64> ThreadImage::push_string_vector_on_stack(const Vector<String>& vec)
{
Vector<u64> user_vec;
for (const auto& item : vec)
{
// Copy each individual string and retrieve a userspace pointer to said copy
u64 addr = TRY(push_mem_on_stack((const u8*)item.chars(), item.length() + 1));
TRY(user_vec.try_append(addr));
}
TRY(user_vec.try_append((u64) nullptr));
// Copy the actual vector of userspace pointers to the stack
return TRY(push_mem_on_stack((u8*)user_vec.data(), user_vec.size() * sizeof(u64)));
}
void ThreadImage::apply(Thread* thread) void ThreadImage::apply(Thread* thread)
{ {
thread->init_regs_user(); thread->init_regs_user();

View File

@ -10,6 +10,8 @@
#include <luna/OwnedPtr.h> #include <luna/OwnedPtr.h>
#include <luna/Result.h> #include <luna/Result.h>
#include <luna/Stack.h> #include <luna/Stack.h>
#include <luna/String.h>
#include <luna/Vector.h>
class Thread; class Thread;
@ -21,6 +23,7 @@ class ThreadImage
static Result<OwnedPtr<ThreadImage>> clone_from_thread(Thread* parent); static Result<OwnedPtr<ThreadImage>> clone_from_thread(Thread* parent);
Result<u64> push_mem_on_stack(const u8* mem, usize size); Result<u64> push_mem_on_stack(const u8* mem, usize size);
Result<u64> push_string_vector_on_stack(const Vector<String>& vec);
void apply(Thread* thread); void apply(Thread* thread);

View File

@ -15,6 +15,7 @@ set(SOURCES
src/init.cpp src/init.cpp
src/dirent.cpp src/dirent.cpp
src/setjmp.cpp src/setjmp.cpp
src/env.cpp
src/sys/stat.cpp src/sys/stat.cpp
src/sys/mman.cpp src/sys/mman.cpp
src/sys/wait.cpp src/sys/wait.cpp

View File

@ -102,7 +102,8 @@ extern "C"
int system(const char*); int system(const char*);
char* getenv(const char*); /* Get the value of an environment variable. */
char* getenv(const char* key);
void qsort(void*, size_t, size_t, int (*)(const void*, const void*)); void qsort(void*, size_t, size_t, int (*)(const void*, const void*));
void* bsearch(const void*, const void*, size_t, size_t, int (*)(const void*, const void*)); void* bsearch(const void*, const void*, size_t, size_t, int (*)(const void*, const void*));

View File

@ -34,8 +34,14 @@ extern "C"
/* Replace the current process with another one. On success, does not return. */ /* Replace the current process with another one. On success, does not return. */
int execl(const char* path, const char* arg, ...); int execl(const char* path, const char* arg, ...);
int execve(const char*, char* const*, char* const*); /* Replace the current process with another one. On success, does not return. */
int execvp(const char*, char* const*); int execve(const char* path, char* const* argv, char* const* envp);
/* Replace the current process with another one. On success, does not return. */
int execvp(const char* name, char* const* argv);
/* Replace the current process with another one. On success, does not return. */
int execvpe(const char* name, char* const* argv, char* const* envp);
/* Call the operating system kernel for a specific service. */ /* Call the operating system kernel for a specific service. */
long syscall(long num, ...); long syscall(long num, ...);

View File

@ -12,12 +12,14 @@ _start:
pushq %rsi pushq %rsi
pushq %rdi pushq %rdi
pushq %rcx
call libc_init call libc_init
# Run the global constructors. # Run the global constructors.
call _init call _init
popq %rdx
popq %rdi popq %rdi
popq %rsi popq %rsi

31
libc/src/env.cpp Normal file
View File

@ -0,0 +1,31 @@
#include <string.h>
extern "C"
{
char** environ = nullptr;
char* getenv(const char* key)
{
char** env = environ;
size_t len = strlen(key);
while (*env)
{
// Retrieve an environment variable from environ.
char* var = *(env++);
// Check for an equals sign, else we just skip this one.
char* delim = strchr(var, '=');
if (!delim) continue;
// Get the length of this variable's key (the part before the equals sign)
size_t key_length = strcspn(var, "=");
if (len != key_length) continue;
// If the keys match, we found it! Return the value (which is just after the equals sign)
if (!memcmp(key, var, key_length)) { return delim + 1; }
}
return nullptr;
}
}

View File

@ -1,10 +1,16 @@
#include <luna/Ignore.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
extern char** environ;
extern "C" extern "C"
{ {
void libc_init() void libc_init(int argc, char** argv, int envc, char** envp)
{ {
ignore(argc, argv, envc);
environ = envp;
stdin = fdopen(STDIN_FILENO, "r"); stdin = fdopen(STDIN_FILENO, "r");
stdout = fdopen(STDOUT_FILENO, "w"); stdout = fdopen(STDOUT_FILENO, "w");
stderr = fdopen(STDERR_FILENO, "w"); stderr = fdopen(STDERR_FILENO, "w");

View File

@ -1,14 +1,18 @@
#include <luna/StringBuilder.h>
#include <luna/Vector.h> #include <luna/Vector.h>
#include <bits/errno-return.h> #include <bits/errno-return.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <unistd.h> #include <unistd.h>
extern "C" long arch_invoke_syscall(long, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); extern "C" long arch_invoke_syscall(long, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
extern char** environ;
extern "C" extern "C"
{ {
pid_t fork(void) pid_t fork(void)
@ -29,10 +33,56 @@ extern "C"
int execv(const char* path, char* const* argv) int execv(const char* path, char* const* argv)
{ {
long rc = syscall(SYS_exec, path, argv); return execve(path, argv, environ);
}
int execve(const char* path, char* const* argv, char* const* envp)
{
long rc = syscall(SYS_execve, path, argv, envp);
__errno_return(rc, int); __errno_return(rc, int);
} }
int execvpe(const char* name, char* const* argv, char* const* envp)
{
if (strchr(name, '/')) return execve(name, argv, envp);
char* path = getenv("PATH");
if (!path) path = const_cast<char*>("/bin:/sbin");
Vector<String> paths;
bool ok = StringView { path }.split(":").try_move_value_or_error(paths, errno);
if (!ok) return -1;
for (const auto& dir : paths)
{
// FIXME: Check for errors.
StringBuilder sb;
sb.add(dir);
sb.add('/');
sb.add(StringView { name });
String file;
ok = sb.string().try_move_value_or_error(file, errno);
if (!ok) return -1;
int err = errno;
execve(file.chars(), argv, envp);
if (errno != ENOENT && errno != EACCES) return -1;
// FIXME: POSIX says that if errno == ENOEXEC, we should run /bin/sh <path>.
if (err == EACCES) errno = err;
}
return -1;
}
int execvp(const char* name, char* const* argv)
{
return execvpe(name, argv, environ);
}
int execl(const char* path, const char* arg, ...) int execl(const char* path, const char* arg, ...)
{ {
va_list ap; va_list ap;

View File

@ -26,6 +26,8 @@ class String
Result<String> substring(usize begin, usize size) const; Result<String> substring(usize begin, usize size) const;
Result<Vector<String>> split(StringView delim) const;
static Result<String> format(const String& fmt, ...); static Result<String> format(const String& fmt, ...);
static Result<String> format(StringView fmt, ...); static Result<String> format(StringView fmt, ...);

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <luna/Result.h> #include <luna/Result.h>
#include <luna/Vector.h>
class String; class String;
@ -37,6 +38,8 @@ class StringView
bool operator==(const char* other) const; bool operator==(const char* other) const;
bool operator==(StringView other) const; bool operator==(StringView other) const;
Result<Vector<String>> split(StringView delim) const;
Iterator begin() const Iterator begin() const
{ {
return m_string; return m_string;

View File

@ -2,7 +2,7 @@
#define enumerate_syscalls(_e) \ #define enumerate_syscalls(_e) \
_e(exit) _e(clock_gettime) _e(mmap) _e(munmap) _e(usleep) _e(open) _e(close) _e(read) _e(getpid) _e(write) \ _e(exit) _e(clock_gettime) _e(mmap) _e(munmap) _e(usleep) _e(open) _e(close) _e(read) _e(getpid) _e(write) \
_e(lseek) _e(mkdir) _e(exec) _e(mknod) _e(fork) _e(waitpid) _e(getppid) _e(fcntl) _e(getdents) _e(lseek) _e(mkdir) _e(execve) _e(mknod) _e(fork) _e(waitpid) _e(getppid) _e(fcntl) _e(getdents)
enum Syscalls enum Syscalls
{ {

View File

@ -75,6 +75,11 @@ Result<String> String::substring(usize begin, usize size) const
return String { dup, size }; return String { dup, size };
} }
Result<Vector<String>> String::split(StringView delim) const
{
return view().split(delim);
}
const char& String::operator[](usize index) const const char& String::operator[](usize index) const
{ {
expect(index < m_length, "index out of range"); expect(index < m_length, "index out of range");

View File

@ -46,6 +46,33 @@ bool StringView::operator==(StringView other) const
return !strcmp(m_string, other.m_string); return !strcmp(m_string, other.m_string);
} }
Result<Vector<String>> StringView::split(StringView delim) const
{
Vector<String> result;
String str;
char* copy = strndup(m_string, m_length);
char* segment = strtok(copy, delim.chars());
if (!segment) goto end;
str = TRY(String::from_cstring(segment));
TRY(result.try_append(move(str)));
while (true)
{
segment = strtok(nullptr, delim.chars());
if (!segment) goto end;
str = TRY(String::from_cstring(segment));
TRY(result.try_append(move(str)));
}
end:
free_impl(copy);
return result;
}
Result<String> StringView::to_string() Result<String> StringView::to_string()
{ {
return String::from_cstring(m_string); return String::from_cstring(m_string);