[libc-commits] [libc] [llvm] [libc] implement popen/pclose (PR #232846)
via libc-commits
libc-commits at lists.llvm.org
Fri Oct 16 10:00:37 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libc
Author: Michael Jones (michaelrj-google)
<details>
<summary>Changes</summary>
This is a deceptively complex set of functions. They need to have
their own FILE subclass to track the PID of the child process and a list
of other open files specifically for popen. I also ended up refactoring
`system` since it shares the fork-and-shell mechanism. The POSIX
definition is here: https://pubs.opengroup.org/onlinepubs/9799919799/functions/popen.html
Assisted-by: Automated tooling, human reviewed.
---
Patch is 46.55 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/232846.diff
27 Files Affected:
- (modified) libc/config/linux/aarch64/entrypoints.txt (+2)
- (modified) libc/config/linux/riscv/entrypoints.txt (+2)
- (modified) libc/config/linux/x86_64/entrypoints.txt (+2)
- (modified) libc/include/stdio.yaml (+13)
- (modified) libc/src/__support/File/file_mode.h (+4)
- (modified) libc/src/__support/File/linux/CMakeLists.txt (+17)
- (modified) libc/src/__support/File/linux/file.cpp (+28-41)
- (modified) libc/src/__support/File/linux/file.h (+56-4)
- (added) libc/src/__support/File/linux/popen_file.cpp (+58)
- (added) libc/src/__support/File/linux/popen_file.h (+66)
- (modified) libc/src/__support/OSUtil/linux/CMakeLists.txt (+19)
- (added) libc/src/__support/OSUtil/linux/shell_fork.h (+79)
- (modified) libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt (+13)
- (added) libc/src/__support/OSUtil/linux/syscall_wrappers/pipe2.h (+37)
- (modified) libc/src/stdio/CMakeLists.txt (+14)
- (modified) libc/src/stdio/linux/CMakeLists.txt (+43)
- (added) libc/src/stdio/linux/pclose.cpp (+53)
- (added) libc/src/stdio/linux/popen.cpp (+131)
- (added) libc/src/stdio/pclose.h (+26)
- (added) libc/src/stdio/popen.h (+26)
- (modified) libc/src/stdlib/linux/CMakeLists.txt (+2-6)
- (modified) libc/src/stdlib/linux/system.cpp (+18-44)
- (modified) libc/src/unistd/linux/CMakeLists.txt (+4-3)
- (modified) libc/src/unistd/linux/pipe2.cpp (+11-9)
- (modified) libc/test/src/stdio/CMakeLists.txt (+23)
- (added) libc/test/src/stdio/popen_pclose_test.cpp (+158)
- (modified) utils/bazel/llvm-project-overlay/libc/BUILD.bazel (+1)
``````````diff
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 13130efdf79bb3..3186fbe0f14a8e 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1332,7 +1332,9 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.stdio.getc_unlocked
libc.src.stdio.getchar
libc.src.stdio.getchar_unlocked
+ libc.src.stdio.pclose
libc.src.stdio.perror
+ libc.src.stdio.popen
libc.src.stdio.putc
libc.src.stdio.putchar
libc.src.stdio.puts
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index e53f61dd43e90a..f9085437e0b2a7 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1513,7 +1513,9 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.stdio.getc_unlocked
libc.src.stdio.getchar
libc.src.stdio.getchar_unlocked
+ libc.src.stdio.pclose
libc.src.stdio.perror
+ libc.src.stdio.popen
libc.src.stdio.putc
libc.src.stdio.putchar
libc.src.stdio.puts
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index dcfa2ef81338e7..325e4e73c0fb03 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1539,7 +1539,9 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.stdio.getc_unlocked
libc.src.stdio.getchar
libc.src.stdio.getchar_unlocked
+ libc.src.stdio.pclose
libc.src.stdio.perror
+ libc.src.stdio.popen
libc.src.stdio.putc
libc.src.stdio.putchar
libc.src.stdio.puts
diff --git a/libc/include/stdio.yaml b/libc/include/stdio.yaml
index c910c53d5f3a79..251840d8670a63 100644
--- a/libc/include/stdio.yaml
+++ b/libc/include/stdio.yaml
@@ -329,12 +329,25 @@ functions:
- posix
return_type: int
arguments: []
+ - name: pclose
+ standards:
+ - posix
+ return_type: int
+ arguments:
+ - type: FILE *
- name: perror
standards:
- stdc
return_type: void
arguments:
- type: const char *
+ - name: popen
+ standards:
+ - posix
+ return_type: FILE *
+ arguments:
+ - type: const char *
+ - type: const char *
- name: printf
standards:
- stdc
diff --git a/libc/src/__support/File/file_mode.h b/libc/src/__support/File/file_mode.h
index 8049516da261f0..1c4c8b7b7e3986 100644
--- a/libc/src/__support/File/file_mode.h
+++ b/libc/src/__support/File/file_mode.h
@@ -86,6 +86,10 @@ class FileMode {
constexpr bool is_valid() const { return file_mode_ != 0; }
+ constexpr bool operator==(const FileMode &other) const {
+ return file_mode_ == other.file_mode_;
+ }
+
constexpr bool is_append() const {
return (file_mode_ & static_cast<Mode>(OpenMode::APPEND)) != 0;
}
diff --git a/libc/src/__support/File/linux/CMakeLists.txt b/libc/src/__support/File/linux/CMakeLists.txt
index 05efc0f6f61395..57d3d5c025e7b7 100644
--- a/libc/src/__support/File/linux/CMakeLists.txt
+++ b/libc/src/__support/File/linux/CMakeLists.txt
@@ -12,7 +12,9 @@ add_object_library(
libc.hdr.sys_stat_macros
libc.hdr.types.FILE
libc.hdr.types.off_t
+ libc.src.__support.alloc_checker
libc.src.__support.CPP.new
+ libc.src.__support.CPP.type_traits
libc.src.__support.OSUtil.linux.syscall_wrappers.close
libc.src.__support.OSUtil.linux.syscall_wrappers.dup3
libc.src.__support.OSUtil.linux.syscall_wrappers.fcntl
@@ -25,6 +27,21 @@ add_object_library(
libc.src.errno.errno
)
+add_object_library(
+ popen_file
+ SRCS
+ popen_file.cpp
+ HDRS
+ popen_file.h
+ DEPENDS
+ .file
+ libc.hdr.types.pid_t
+ libc.src.__support.CPP.new
+ libc.src.__support.File.file
+ libc.src.__support.macros.config
+ libc.src.__support.threads.mutex
+)
+
add_object_library(
linux_dir
SRCS
diff --git a/libc/src/__support/File/linux/file.cpp b/libc/src/__support/File/linux/file.cpp
index 1d360c15bfa834..0bc3ba37a4a91d 100644
--- a/libc/src/__support/File/linux/file.cpp
+++ b/libc/src/__support/File/linux/file.cpp
@@ -1,10 +1,15 @@
-//===--- Implementation of the Linux specialization of 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 the Linux specialization of File.
+///
+//===----------------------------------------------------------------------===//
#include "file.h"
#include "file_flags.h"
@@ -21,7 +26,6 @@
#include "src/__support/OSUtil/linux/syscall_wrappers/open.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/read.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/write.h"
-#include "src/__support/alloc-checker.h"
#include "src/__support/libc_errno.h" // For error macros
#include "src/__support/macros/config.h"
@@ -50,20 +54,22 @@ ErrorOr<off_t> linux_file_seek(File *f, off_t offset, int whence) {
return linux_syscalls::lseek(lf->get_fd(), offset, whence);
}
+int LinuxFile::close_fd() {
+ if (fd < 0)
+ return EBADF;
+ // Linux closes the file descriptor early in the syscall, so we assume it's
+ // always closed after the call. That means callers should also delete the
+ // file object on error.
+ auto result = linux_syscalls::close(fd);
+ if (!result)
+ return result.error();
+ return 0;
+}
+
int linux_file_close(File *f) {
File::remove_file(f);
auto *lf = reinterpret_cast<LinuxFile *>(f);
- int retval = 0;
- if (lf->get_fd() >= 0) {
- // Linux closes the file descriptor early in the syscall, so we assume it's
- // always closed after the call. That means we should also delete the
- // LinuxFile on error.
- auto result = linux_syscalls::close(lf->get_fd());
- if (!result)
- retval = result.error();
- } else {
- retval = EBADF;
- }
+ int retval = lf->close_fd();
delete lf;
return retval;
}
@@ -111,20 +117,11 @@ ErrorOr<File *> openfile(const char *path, const char *mode) {
if (!fd)
return Error(fd.error());
- uint8_t *buffer;
- {
- AllocChecker ac;
- buffer = new (ac) uint8_t[File::DEFAULT_BUFFER_SIZE];
- if (!ac)
- return Error(ENOMEM);
- }
- AllocChecker ac;
- auto *file = new (ac) LinuxFile(fd.value(), buffer, File::DEFAULT_BUFFER_SIZE,
- _IOFBF, true, file_mode);
- if (!ac)
- return Error(ENOMEM);
- File::add_file(file);
- return file;
+ ErrorOr<LinuxFile *> file = LinuxFile::create_buffered(fd.value(), file_mode);
+ if (!file)
+ return Error(file.error());
+ File::add_file(file.value());
+ return file.value();
}
static ErrorOr<int> set_close_on_exec(int fd) {
@@ -172,20 +169,10 @@ ErrorOr<LinuxFile *> create_file_from_fd(int fd, const char *mode) {
}
}
- uint8_t *buffer;
- {
- AllocChecker ac;
- buffer = new (ac) uint8_t[File::DEFAULT_BUFFER_SIZE];
- if (!ac) {
- return Error(ENOMEM);
- }
- }
- AllocChecker ac;
- auto *file = new (ac)
- LinuxFile(fd, buffer, File::DEFAULT_BUFFER_SIZE, _IOFBF, true, file_mode);
- if (!ac) {
- return Error(ENOMEM);
- }
+ ErrorOr<LinuxFile *> new_file = LinuxFile::create_buffered(fd, file_mode);
+ if (!new_file)
+ return Error(new_file.error());
+ LinuxFile *file = new_file.value();
File::add_file(file);
if (do_seek) {
result = file->seek(0, SEEK_END);
diff --git a/libc/src/__support/File/linux/file.h b/libc/src/__support/File/linux/file.h
index 539957b8b22c8c..d1ea9da9294f4c 100644
--- a/libc/src/__support/File/linux/file.h
+++ b/libc/src/__support/File/linux/file.h
@@ -1,14 +1,27 @@
-//===--- Linux specialization of the File data structure ------------------===//
+//===----------------------------------------------------------------------===//
//
// 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
+/// Linux specialization of the File data structure.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_FILE_LINUX_FILE_H
+#define LLVM_LIBC_SRC___SUPPORT_FILE_LINUX_FILE_H
+#include "hdr/stdio_macros.h"
#include "hdr/types/off_t.h"
+#include "src/__support/CPP/new.h"
+#include "src/__support/CPP/type_traits.h"
#include "src/__support/File/file.h"
#include "src/__support/File/file_mode.h"
+#include "src/__support/alloc-checker.h"
+#include "src/__support/error_or.h"
#include "src/__support/macros/config.h"
namespace LIBC_NAMESPACE_DECL {
@@ -21,12 +34,49 @@ int linux_file_close(File *);
class LinuxFile : public File {
int fd;
+ friend int linux_file_close(File *);
+
+protected:
+ // For subclasses which need to do extra cleanup when the file is closed.
+ constexpr LinuxFile(int file_descriptor, uint8_t *buffer, size_t buffer_size,
+ int buffer_mode, bool owned, FileMode mode,
+ CloseFunc *close_func)
+ : File(&linux_file_write, &linux_file_read, &linux_file_seek, close_func,
+ buffer, buffer_size, buffer_mode, owned, mode),
+ fd(file_descriptor) {}
+
+ // Closes the underlying file descriptor. Returns 0 on success, or an error
+ // code. This does not free the file object.
+ int close_fd();
+
public:
constexpr LinuxFile(int file_descriptor, uint8_t *buffer, size_t buffer_size,
int buffer_mode, bool owned, FileMode mode)
- : File(&linux_file_write, &linux_file_read, &linux_file_seek,
- &linux_file_close, buffer, buffer_size, buffer_mode, owned, mode),
- fd(file_descriptor) {}
+ : LinuxFile(file_descriptor, buffer, buffer_size, buffer_mode, owned,
+ mode, &linux_file_close) {}
+
+ // Allocates a FileT owning a new default-size buffer. FileT must be
+ // LinuxFile or a subclass with LinuxFile's constructor signature.
+ template <typename FileT = LinuxFile>
+ LIBC_INLINE static ErrorOr<FileT *> create_buffered(int fd, FileMode mode) {
+ static_assert(cpp::is_base_of_v<LinuxFile, FileT>,
+ "FileT must be LinuxFile or derive from it");
+ uint8_t *buffer;
+ {
+ AllocChecker ac;
+ buffer = new (ac) uint8_t[DEFAULT_BUFFER_SIZE];
+ if (!ac)
+ return Error(ENOMEM);
+ }
+ AllocChecker ac;
+ auto *file = new (ac) FileT(fd, buffer, DEFAULT_BUFFER_SIZE, _IOFBF,
+ /*owned=*/true, mode);
+ if (!ac) {
+ delete buffer;
+ return Error(ENOMEM);
+ }
+ return file;
+ }
int get_fd() const { return fd; }
void set_fd(int new_fd) { fd = new_fd; }
@@ -38,3 +88,5 @@ class LinuxFile : public File {
ErrorOr<LinuxFile *> create_file_from_fd(int fd, const char *mode);
} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_FILE_LINUX_FILE_H
diff --git a/libc/src/__support/File/linux/popen_file.cpp b/libc/src/__support/File/linux/popen_file.cpp
new file mode 100644
index 00000000000000..18435e47bb2497
--- /dev/null
+++ b/libc/src/__support/File/linux/popen_file.cpp
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 the Linux File specialization for popen streams.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/File/linux/popen_file.h"
+
+#include "src/__support/CPP/new.h"
+#include "src/__support/File/file.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/threads/mutex.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+PopenFile *PopenFile::popen_list = nullptr;
+Mutex PopenFile::popen_list_lock(/*timed=*/false, /*recursive=*/false,
+ /*robust=*/false, /*pshared=*/false);
+
+void PopenFile::add_to_popen_list_unlocked(PopenFile *f) {
+ f->popen_prev = nullptr;
+ f->popen_next = popen_list;
+ if (popen_list != nullptr)
+ popen_list->popen_prev = f;
+ popen_list = f;
+}
+
+void PopenFile::close_popen_list_fds_unlocked() {
+ for (PopenFile *f = popen_list; f != nullptr; f = f->popen_next)
+ f->close_fd();
+}
+
+int PopenFile::popen_file_close(File *f) {
+ auto *pf = reinterpret_cast<PopenFile *>(f);
+
+ lock_popen_list();
+ if (pf->popen_prev != nullptr)
+ pf->popen_prev->popen_next = pf->popen_next;
+ if (pf->popen_next != nullptr)
+ pf->popen_next->popen_prev = pf->popen_prev;
+ if (popen_list == pf)
+ popen_list = pf->popen_next;
+ unlock_popen_list();
+
+ File::remove_file(f);
+ int retval = pf->close_fd();
+ delete pf;
+ return retval;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/File/linux/popen_file.h b/libc/src/__support/File/linux/popen_file.h
new file mode 100644
index 00000000000000..9c959b069c52dc
--- /dev/null
+++ b/libc/src/__support/File/linux/popen_file.h
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Linux File specialization for streams created by popen.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_FILE_LINUX_POPEN_FILE_H
+#define LLVM_LIBC_SRC___SUPPORT_FILE_LINUX_POPEN_FILE_H
+
+#include "hdr/types/pid_t.h"
+#include "src/__support/File/file.h"
+#include "src/__support/File/file_mode.h"
+#include "src/__support/File/linux/file.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/threads/mutex.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// A LinuxFile connected to a pipe to a child process created by popen. Open
+// popen streams are kept on their own list, so that each new popen child can
+// close the streams of earlier popen calls.
+class PopenFile : public LinuxFile {
+ pid_t pid = -1;
+ PopenFile *popen_prev = nullptr;
+ PopenFile *popen_next = nullptr;
+
+ static PopenFile *popen_list;
+ static Mutex popen_list_lock;
+
+ // Removes |f| from the popen list, then closes and frees it like a
+ // LinuxFile. This does not wait for the child process.
+ static int popen_file_close(File *f);
+
+public:
+ PopenFile(int file_descriptor, uint8_t *buffer, size_t buffer_size,
+ int buffer_mode, bool owned, FileMode mode)
+ : LinuxFile(file_descriptor, buffer, buffer_size, buffer_mode, owned,
+ mode, &popen_file_close) {}
+
+ pid_t get_pid() const { return pid; }
+ void set_pid(pid_t new_pid) { pid = new_pid; }
+
+ // The popen list lock must be held while calling the *_unlocked functions
+ // below. popen holds it from before forking until the new stream is on the
+ // list, so that the child sees a consistent list.
+ static void lock_popen_list() { popen_list_lock.lock(); }
+ static void unlock_popen_list() { popen_list_lock.unlock(); }
+
+ // Adds |f| to the popen list.
+ static void add_to_popen_list_unlocked(PopenFile *f);
+
+ // Closes the file descriptors of every stream on the popen list without
+ // freeing anything. This is meant to be called in a newly forked child.
+ static void close_popen_list_fds_unlocked();
+};
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_FILE_LINUX_POPEN_FILE_H
diff --git a/libc/src/__support/OSUtil/linux/CMakeLists.txt b/libc/src/__support/OSUtil/linux/CMakeLists.txt
index e01f079c6780e5..728166d37be6de 100644
--- a/libc/src/__support/OSUtil/linux/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/CMakeLists.txt
@@ -66,6 +66,25 @@ add_header_library(
libc.src.__support.CPP.string_view
)
+add_header_library(
+ shell_fork
+ HDRS
+ shell_fork.h
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.signal_macros
+ libc.hdr.types.pid_t
+ libc.src.__support.common
+ libc.src.__support.CPP.functional
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.OSUtil.linux.syscall_wrappers.clone3
+ libc.src.__support.OSUtil.linux.syscall_wrappers.execle
+ libc.src.__support.OSUtil.linux.syscall_wrappers.wait4
+ libc.src.unistd.environ
+)
+
add_header_library(
sysinfo
HDRS
diff --git a/libc/src/__support/OSUtil/linux/shell_fork.h b/libc/src/__support/OSUtil/linux/shell_fork.h
new file mode 100644
index 00000000000000..3dd64fad34e80a
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/shell_fork.h
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Helpers for running a command with the shell in a child process, shared by
+/// functions like system and popen.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SHELL_FORK_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SHELL_FORK_H
+
+#include "hdr/errno_macros.h"
+#include "hdr/signal_macros.h"
+#include "hdr/types/pid_t.h"
+#include "src/__support/CPP/functional.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/wait4.h"
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include "src/unistd/environ.h"
+
+#include <linux/sched.h> // For struct clone_args
+
+namespace LIBC_NAMESPACE_DECL {
+
+LIBC_INLINE_VAR constexpr char SHELL_PATH[] = "/bin/sh";
+
+// Work to do in the child process after forking and before executing the
+// shell. Returns false if the setup failed and the shell should not be run.
+using ShellChildSetup = cpp::function<bool()>;
+
+// Forks a child process which runs |setup| and then executes
+// "sh -c |command|" with the current environment. If |setup| or the exec
+// fails, the child exits with status 127. Returns the child's pid in the
+// parent.
+LIBC_INLINE ErrorOr<pid_t> shell_fork(const char *command,
+ ShellChildSetup setup) {
+ struct clone_args cl_args{};
+ cl_args.exit_signal = SIGCHLD;
+ ErrorOr<pid_t> fork_res = linux_syscalls::clone3(&cl_args, sizeof(cl_args));
+ if (!fork_res.has_value())
+ return Error(fork_res.error());
+
+ if (fork_res.value() == 0) {
+ // 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.
+ if (setup())
+ linux_syscalls::execle(SHELL_PATH, "sh", "-c", command, 0, environ);
+ internal::exit(127);
+ }
+
+ return fork_res.value();
+}
+
+// Waits for the child process |pid| to terminate, retrying if interrupted by
+// a signal. Returns the child's wait status.
+LIBC_INLINE ErrorOr<int> shell_wait(pid_t pid) {
+ int status = 0;
+ while (true) {
+ ErrorOr<pid_t> wait_res = linux_syscalls::wait4(pid, &status, 0, nullptr);
+ if (wait_res.has_value())
+ return status;
+ if (wait_res.error() != EINTR)
+ return Error(wait_res.error());
+ }
+}
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SHELL_FORK_H
diff --git a/libc/src/__support/OSUtil/linux/s...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/232846
More information about the libc-commits
mailing list