blob: f40e1f8756b15532e0aa4c924d021ec3b8828ba2 [file]
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Implementation of system for Linux.
///
//===----------------------------------------------------------------------===//
#include "src/stdlib/system.h"
#include "hdr/errno_macros.h"
#include "hdr/signal_macros.h"
#include "hdr/types/pid_t.h"
#include "hdr/types/sigset_t.h"
#include "src/__support/OSUtil/exit.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/clone3.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/execle.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/rt_sigaction.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/rt_sigprocmask.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/wait4.h"
#include "src/__support/common.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include "src/signal/linux/signal_utils.h"
#include "src/unistd/environ.h"
#include <linux/sched.h> // For struct clone_args
namespace LIBC_NAMESPACE_DECL {
const char *SHELL_PATH = "/bin/sh";
LLVM_LIBC_FUNCTION(int, system, (const char *command)) {
if (command == nullptr)
return 1;
KernelSigaction sa_ign{};
sa_ign.sa_handler = SIG_IGN;
KernelSigaction orig_int{};
KernelSigaction orig_quit{};
if (auto res = linux_syscalls::rt_sigaction(SIGINT, &sa_ign, &orig_int);
!res.has_value()) {
libc_errno = res.error();
return -1;
}
if (auto res = linux_syscalls::rt_sigaction(SIGQUIT, &sa_ign, &orig_quit);
!res.has_value()) {
linux_syscalls::rt_sigaction(SIGINT, &orig_int, nullptr);
libc_errno = res.error();
return -1;
}
sigset_t block_mask{};
add_signal(block_mask, SIGCHLD);
sigset_t orig_mask{};
if (auto res =
linux_syscalls::rt_sigprocmask(SIG_BLOCK, &block_mask, &orig_mask);
!res.has_value()) {
linux_syscalls::rt_sigaction(SIGINT, &orig_int, nullptr);
linux_syscalls::rt_sigaction(SIGQUIT, &orig_quit, nullptr);
libc_errno = res.error();
return -1;
}
struct clone_args cl_args{};
cl_args.exit_signal = SIGCHLD;
auto fork_res = linux_syscalls::clone3(&cl_args, sizeof(cl_args));
if (!fork_res.has_value()) {
linux_syscalls::rt_sigaction(SIGINT, &orig_int, nullptr);
linux_syscalls::rt_sigaction(SIGQUIT, &orig_quit, nullptr);
linux_syscalls::rt_sigprocmask(SIG_SETMASK, &orig_mask, nullptr);
libc_errno = fork_res.error();
return -1;
}
pid_t pid = fork_res.value();
if (pid == 0) {
linux_syscalls::rt_sigaction(SIGINT, &orig_int, nullptr);
linux_syscalls::rt_sigaction(SIGQUIT, &orig_quit, nullptr);
linux_syscalls::rt_sigprocmask(SIG_SETMASK, &orig_mask, nullptr);
// Error checking isn't helpful since this is the forked process, so we
// can't set errno. All we can meaningfully do is exit with status 127.
linux_syscalls::execle(SHELL_PATH, "sh", "-c", command, 0, environ);
internal::exit(127);
}
int status = 0;
int wait_ret = 0;
while (true) {
if (auto wait_res = linux_syscalls::wait4(pid, &status, 0, nullptr);
!wait_res.has_value()) {
if (wait_res.error() == EINTR)
continue;
wait_ret = -1;
libc_errno = wait_res.error();
break;
}
wait_ret = status;
break;
}
linux_syscalls::rt_sigaction(SIGINT, &orig_int, nullptr);
linux_syscalls::rt_sigaction(SIGQUIT, &orig_quit, nullptr);
linux_syscalls::rt_sigprocmask(SIG_SETMASK, &orig_mask, nullptr);
return wait_ret;
}
} // namespace LIBC_NAMESPACE_DECL