[libc-commits] [libc] [llvm] [libc] implement popen/pclose (PR #232846)

Jeff Bailey via libc-commits libc-commits at lists.llvm.org
Mon Oct 19 02:55:39 PDT 2026


================
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 POSIX popen function for Linux.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/stdio/popen.h"
+
+#include "hdr/errno_macros.h"
+#include "hdr/fcntl_macros.h"
+#include "hdr/types/FILE.h"
+#include "hdr/types/pid_t.h"
+#include "hdr/unistd_macros.h"
+#include "src/__support/File/file.h"
+#include "src/__support/File/file_mode.h"
+#include "src/__support/File/linux/file.h"
+#include "src/__support/File/linux/popen_file.h"
+#include "src/__support/OSUtil/linux/shell_fork.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/close.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/dup2.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/fcntl.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/pipe2.h"
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+namespace {
+
+// A pipe only goes one way, so these are the only modes popen accepts.
+constexpr FileMode VALID_POPEN_MODES[] = {FileMode("r"), FileMode("w"),
+                                          FileMode("re"), FileMode("we")};
+
+// TODO: Simplify this with a cpp::find (or similar) helper in
+// src/__support/CPP/algorithm.h once one exists.
+bool is_valid_popen_mode(const FileMode &file_mode) {
+  for (const FileMode &valid_mode : VALID_POPEN_MODES)
+    if (file_mode == valid_mode)
+      return true;
+  return false;
+}
+
+// Clears FD_CLOEXEC on |fd|, leaving its other descriptor flags unchanged.
+ErrorOr<int> clear_close_on_exec(int fd) {
+  ErrorOr<int> flags = linux_syscalls::fcntl(fd, F_GETFD);
+  if (!flags)
+    return flags;
+  return linux_syscalls::fcntl(
+      fd, F_SETFD, reinterpret_cast<void *>(flags.value() & ~FD_CLOEXEC));
+}
+
+// Makes |fd| available as |target_fd| in a process that is about to exec.
+// Both ends of the pipe are created close-on-exec, so if |fd| is already
+// |target_fd| its FD_CLOEXEC flag has to be cleared instead.
+bool move_fd_for_exec(int fd, int target_fd) {
+  if (fd == target_fd)
+    return clear_close_on_exec(fd).has_value();
+  return linux_syscalls::dup2(fd, target_fd).has_value();
+}
+
+} // namespace
+
+LLVM_LIBC_FUNCTION(::FILE *, popen, (const char *command, const char *mode)) {
+  const FileMode file_mode(mode);
+  if (!is_valid_popen_mode(file_mode)) {
+    libc_errno = EINVAL;
+    return nullptr;
+  }
+  const bool read = file_mode.is_read();
+
+  // Both ends start out close-on-exec so that no other child process can
+  // inherit them. The child moves its end onto stdin or stdout before exec.
+  int pipe_fds[2];
+  if (ErrorOr<int> res = linux_syscalls::pipe2(pipe_fds, O_CLOEXEC); !res) {
+    libc_errno = res.error();
+    return nullptr;
+  }
+  const int parent_fd = read ? pipe_fds[0] : pipe_fds[1];
+  const int child_fd = read ? pipe_fds[1] : pipe_fds[0];
+  const int child_target_fd = read ? STDOUT_FILENO : STDIN_FILENO;
----------------
kaladron wrote:

I'm hoping to nail this down properly with clang-tidy checks soon.  I'm working with @lntue to get it in enforcement mode in presubmit GitHub Actions with a tiny set, and then want to add in all the things that we don't want to have to review in code review.  My plan at each step is to update the whole codebase to be compliant.

https://github.com/llvm/llvm-project/pull/232846


More information about the libc-commits mailing list