summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibCore/System.cpp126
-rw-r--r--Userland/Libraries/LibCore/System.h5
2 files changed, 130 insertions, 1 deletions
diff --git a/Userland/Libraries/LibCore/System.cpp b/Userland/Libraries/LibCore/System.cpp
index 7f91c311f7..8ebc0c0962 100644
--- a/Userland/Libraries/LibCore/System.cpp
+++ b/Userland/Libraries/LibCore/System.cpp
@@ -1,12 +1,14 @@
/*
* Copyright (c) 2021-2022, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021-2022, Kenneth Myhra <kennethmyhra@gmail.com>
- * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
+ * Copyright (c) 2021-2022, Sam Atkins <atkinssj@serenityos.org>
* Copyright (c) 2022, Matthias Zimmerman <matthias291999@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include <AK/FixedArray.h>
+#include <AK/ScopedValueRollback.h>
#include <AK/StdLibExtras.h>
#include <AK/String.h>
#include <AK/Vector.h>
@@ -906,6 +908,128 @@ ErrorOr<void> adjtime(const struct timeval* delta, struct timeval* old_delta)
#endif
}
+ErrorOr<void> exec(StringView filename, Span<StringView> arguments, SearchInPath search_in_path, Optional<Span<StringView>> environment)
+{
+#ifdef __serenity__
+ Syscall::SC_execve_params params;
+
+ auto argument_strings = TRY(FixedArray<Syscall::StringArgument>::try_create(arguments.size()));
+ for (size_t i = 0; i < arguments.size(); ++i) {
+ argument_strings[i] = { arguments[i].characters_without_null_termination(), arguments[i].length() };
+ }
+ params.arguments.strings = argument_strings.data();
+ params.arguments.length = argument_strings.size();
+
+ size_t env_count = 0;
+ if (environment.has_value()) {
+ env_count = environment->size();
+ } else {
+ for (size_t i = 0; environ[i]; ++i)
+ ++env_count;
+ }
+
+ auto environment_strings = TRY(FixedArray<Syscall::StringArgument>::try_create(env_count));
+ if (environment.has_value()) {
+ for (size_t i = 0; i < env_count; ++i) {
+ environment_strings[i] = { environment->at(i).characters_without_null_termination(), environment->at(i).length() };
+ }
+ } else {
+ for (size_t i = 0; i < env_count; ++i) {
+ environment_strings[i] = { environ[i], strlen(environ[i]) };
+ }
+ }
+ params.environment.strings = environment_strings.data();
+ params.environment.length = environment_strings.size();
+
+ auto run_exec = [](Syscall::SC_execve_params& params) -> ErrorOr<void> {
+ int rc = syscall(Syscall::SC_execve, &params);
+ if (rc < 0)
+ return Error::from_syscall("exec"sv, rc);
+ return {};
+ };
+
+ if (search_in_path == SearchInPath::Yes && !filename.contains('/')) {
+ StringView path = getenv("PATH");
+ if (path.is_empty())
+ path = "/bin:/usr/bin";
+ auto parts = path.split_view(':');
+ for (auto& part : parts) {
+ auto candidate = String::formatted("{}/{}", part, filename);
+ params.path = { candidate.characters(), candidate.length() };
+ auto result = run_exec(params);
+ if (result.is_error()) {
+ if (result.error().code() != ENOENT)
+ return result.error();
+ } else {
+ VERIFY_NOT_REACHED();
+ }
+ }
+ return Error::from_syscall("exec"sv, -ENOENT);
+ } else {
+ params.path = { filename.characters_without_null_termination(), filename.length() };
+ }
+
+ TRY(run_exec(params));
+ VERIFY_NOT_REACHED();
+#else
+ String filename_string { filename };
+
+ auto argument_strings = TRY(FixedArray<String>::try_create(arguments.size()));
+ auto argv = TRY(FixedArray<char*>::try_create(arguments.size() + 1));
+ for (size_t i = 0; i < arguments.size(); ++i) {
+ argument_strings[i] = arguments[i].to_string();
+ argv[i] = const_cast<char*>(argument_strings[i].characters());
+ }
+ argv[arguments.size()] = nullptr;
+
+ int rc = 0;
+ if (environment.has_value()) {
+ auto environment_strings = TRY(FixedArray<String>::try_create(environment->size()));
+ auto envp = TRY(FixedArray<char*>::try_create(environment->size() + 1));
+ for (size_t i = 0; i < environment->size(); ++i) {
+ environment_strings[i] = environment->at(i).to_string();
+ envp[i] = const_cast<char*>(environment_strings[i].characters());
+ }
+ envp[environment->size()] = nullptr;
+
+ if (search_in_path == SearchInPath::Yes && !filename.contains('/')) {
+# if defined(__APPLE__) || defined(__FreeBSD__)
+ // These BSDs don't support execvpe(), so we'll have to manually search the PATH.
+ // This is copy-pasted from LibC's execvpe() with minor changes.
+ ScopedValueRollback errno_rollback(errno);
+ String path = getenv("PATH");
+ if (path.is_empty())
+ path = "/bin:/usr/bin";
+ auto parts = path.split(':');
+ for (auto& part : parts) {
+ auto candidate = String::formatted("{}/{}", part, filename);
+ rc = ::execve(candidate.characters(), argv.data(), envp.data());
+ if (rc < 0 && errno != ENOENT) {
+ errno_rollback.set_override_rollback_value(errno);
+ return Error::from_syscall("exec"sv, rc);
+ }
+ }
+ errno_rollback.set_override_rollback_value(ENOENT);
+# else
+ rc = ::execvpe(filename_string.characters(), argv.data(), envp.data());
+# endif
+ } else {
+ rc = ::execve(filename_string.characters(), argv.data(), envp.data());
+ }
+
+ } else {
+ if (search_in_path == SearchInPath::Yes)
+ rc = ::execvp(filename_string.characters(), argv.data());
+ else
+ rc = ::execv(filename_string.characters(), argv.data());
+ }
+
+ if (rc < 0)
+ return Error::from_syscall("exec"sv, rc);
+ VERIFY_NOT_REACHED();
+#endif
+}
+
ErrorOr<int> socket(int domain, int type, int protocol)
{
auto fd = ::socket(domain, type, protocol);
diff --git a/Userland/Libraries/LibCore/System.h b/Userland/Libraries/LibCore/System.h
index 71dcfc6b4c..49a0a2d288 100644
--- a/Userland/Libraries/LibCore/System.h
+++ b/Userland/Libraries/LibCore/System.h
@@ -127,6 +127,11 @@ ErrorOr<void> utime(StringView path, Optional<struct utimbuf>);
ErrorOr<struct utsname> uname();
ErrorOr<Array<int, 2>> pipe2(int flags);
ErrorOr<void> adjtime(const struct timeval* delta, struct timeval* old_delta);
+enum class SearchInPath {
+ No,
+ Yes,
+};
+ErrorOr<void> exec(StringView filename, Span<StringView> arguments, SearchInPath, Optional<Span<StringView>> environment = {});
ErrorOr<int> socket(int domain, int type, int protocol);
ErrorOr<void> bind(int sockfd, struct sockaddr const*, socklen_t);