[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