diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 7d69099e3cb9d..de3b7bcbae23a 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -290,6 +290,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.unistd.link libc.src.unistd.linkat libc.src.unistd.lseek + libc.src.unistd.pipe2 libc.src.unistd.pread libc.src.unistd.pwrite libc.src.unistd.read diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 4fb31c593b9dc..702894b8a21d2 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -297,6 +297,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.unistd.link libc.src.unistd.linkat libc.src.unistd.lseek + libc.src.unistd.pipe2 libc.src.unistd.pread libc.src.unistd.pwrite libc.src.unistd.read diff --git a/libc/include/llvm-libc-macros/linux/fcntl-macros.h b/libc/include/llvm-libc-macros/linux/fcntl-macros.h index 1d4e5bbbdc770..a86e51f89fb95 100644 --- a/libc/include/llvm-libc-macros/linux/fcntl-macros.h +++ b/libc/include/llvm-libc-macros/linux/fcntl-macros.h @@ -29,6 +29,12 @@ #define O_NOFOLLOW 00400000 #endif +#ifdef __aarch64__ +#define O_DIRECT 0200000 +#else +#define O_DIRECT 00040000 +#endif + #define O_TRUNC 00001000 #define O_TMPFILE (020000000 | O_DIRECTORY) diff --git a/libc/include/llvm-libc-macros/linux/watch-queue-macros.h b/libc/include/llvm-libc-macros/linux/watch-queue-macros.h new file mode 100644 index 0000000000000..d79f13354c53d --- /dev/null +++ b/libc/include/llvm-libc-macros/linux/watch-queue-macros.h @@ -0,0 +1,20 @@ +//===-- Definition of macros from watch-queue.h ---------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// References +// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/watch_queue.h +// https://kernelnewbies.org/Linux_5.8#Core_.28various.29 +// https://docs.kernel.org/core-api/watch_queue.html + +#ifndef LLVM_LIBC_MACROS_LINUX_WATCH_QUEUE_MACROS_H +#define LLVM_LIBC_MACROS_LINUX_WATCH_QUEUE_MACROS_H + +#define O_NOTIFICATION_PIPE \ + O_EXCL /* Parameter to pipe2() selecting notification pipe */ + +#endif // LLVM_LIBC_MACROS_LINUX_WATCH_QUEUE_MACROS_H diff --git a/libc/spec/linux.td b/libc/spec/linux.td index f91f55ddac784..744a920f05f68 100644 --- a/libc/spec/linux.td +++ b/libc/spec/linux.td @@ -264,6 +264,20 @@ def Linux : StandardSpec<"Linux"> { ] >; + HeaderSpec UniStd = HeaderSpec< + "unistd.h", + [], // Macros + [], // Types + [], // Enumerations + [ + FunctionSpec< + "pipe2", + RetValSpec, + [ArgSpec, ArgSpec] // TODO: use int[2] array for the first ArgSpec when possible + >, + ] + >; + let Headers = [ Errno, SysEpoll, @@ -272,5 +286,6 @@ def Linux : StandardSpec<"Linux"> { SysRandom, SysTime, Signal, + UniStd ]; } diff --git a/libc/src/unistd/CMakeLists.txt b/libc/src/unistd/CMakeLists.txt index e22b0e1872caa..c63f170df29b9 100644 --- a/libc/src/unistd/CMakeLists.txt +++ b/libc/src/unistd/CMakeLists.txt @@ -149,6 +149,13 @@ add_entrypoint_object( .${LIBC_TARGET_OS}.lseek ) +add_entrypoint_object( + pipe2 + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.pipe2 +) + add_entrypoint_object( pread ALIAS diff --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt index df85d44e9e9ed..3496d32a18f2e 100644 --- a/libc/src/unistd/linux/CMakeLists.txt +++ b/libc/src/unistd/linux/CMakeLists.txt @@ -273,6 +273,19 @@ add_entrypoint_object( libc.src.errno.errno ) +add_entrypoint_object( + pipe2 + SRCS + pipe2.cpp + HDRS + ../pipe2.h + DEPENDS + libc.include.unistd + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + add_entrypoint_object( pread SRCS diff --git a/libc/src/unistd/linux/pipe2.cpp b/libc/src/unistd/linux/pipe2.cpp new file mode 100644 index 0000000000000..b9c7d176c2b91 --- /dev/null +++ b/libc/src/unistd/linux/pipe2.cpp @@ -0,0 +1,33 @@ +//===-- Linux implementation of pipe2 -------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/unistd/pipe2.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" +#include "src/errno/libc_errno.h" + +#include // For syscall numbers. + +namespace LIBC_NAMESPACE { + +LLVM_LIBC_FUNCTION(int, pipe2, (int pipefd[2], int flags)) { + int ret; +#ifdef SYS_pipe2 + ret = LIBC_NAMESPACE::syscall_impl(SYS_pipe2, pipefd, flags); +#else +#error "pipe2 not available." +#endif + if (ret < 0) { + libc_errno = -ret; + return -1; + } + return ret; +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/src/unistd/pipe2.h b/libc/src/unistd/pipe2.h new file mode 100644 index 0000000000000..9eec6e1a157d8 --- /dev/null +++ b/libc/src/unistd/pipe2.h @@ -0,0 +1,18 @@ +//===-- Implementation header for pipe2 -------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_UNISTD_PIPE2_H +#define LLVM_LIBC_SRC_UNISTD_PIPE2_H + +namespace LIBC_NAMESPACE { + +int pipe2(int pipefd[2], int flags); + +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_UNISTD_PIPE2_H diff --git a/libc/test/src/unistd/CMakeLists.txt b/libc/test/src/unistd/CMakeLists.txt index 3a7fe6f45c091..8734216756e30 100644 --- a/libc/test/src/unistd/CMakeLists.txt +++ b/libc/test/src/unistd/CMakeLists.txt @@ -444,3 +444,20 @@ add_libc_test( libc.src.stdio.fopencookie libc.src.stdio.fflush ) + +add_libc_unittest( + pipe2_test + SUITE + libc_unistd_unittests + SRCS + pipe2_test.cpp + DEPENDS + libc.include.errno + libc.include.unistd + libc.src.errno.errno + libc.src.unistd.close + libc.src.unistd.pipe2 + libc.src.unistd.read + libc.src.unistd.write + libc.test.UnitTest.ErrnoSetterMatcher +) diff --git a/libc/test/src/unistd/pipe2_test.cpp b/libc/test/src/unistd/pipe2_test.cpp new file mode 100644 index 0000000000000..f460dec4b749a --- /dev/null +++ b/libc/test/src/unistd/pipe2_test.cpp @@ -0,0 +1,93 @@ +//===-- Unittests for pipe2 -----------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "include/llvm-libc-macros/linux/fcntl-macros.h" +#include "include/llvm-libc-macros/linux/watch-queue-macros.h" +#include "src/errno/libc_errno.h" +#include "src/unistd/close.h" +#include "src/unistd/pipe2.h" +#include "src/unistd/read.h" +#include "src/unistd/write.h" +#include "test/UnitTest/ErrnoSetterMatcher.h" +#include "test/UnitTest/Test.h" + +#include + +TEST(LlvmLibcPipe2Test, Pipe2CreationTest) { + int pipefd[2]; + int flags; + + LIBC_NAMESPACE::libc_errno = 0; + using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds; + +// Create pipe(2) with all valid flags set +#ifdef CONFIG_WATCH_QUEUE + flags = O_CLOEXEC | O_NONBLOCK | O_DIRECT | O_NOTIFICATION_PIPE; +#else + flags = O_CLOEXEC | O_NONBLOCK | O_DIRECT; +#endif + ASSERT_NE(LIBC_NAMESPACE::pipe2(pipefd, flags), -1); + ASSERT_ERRNO_SUCCESS(); + + // Check if file descriptors are distinct and valid + ASSERT_GE(pipefd[0], 0); + ASSERT_GE(pipefd[1], 0); + ASSERT_NE(pipefd[0], pipefd[1]); + + // Check file status flags associated with pipe file descriptors + ASSERT_TRUE((fcntl(pipefd[0], F_GETFL) & flags) != 0); + ASSERT_TRUE((fcntl(pipefd[1], F_GETFL) & flags) != 0); + + // Close the pipe file descriptors + ASSERT_NE(LIBC_NAMESPACE::close(pipefd[0]), -1); + ASSERT_ERRNO_SUCCESS(); + ASSERT_NE(LIBC_NAMESPACE::close(pipefd[1]), -1); + ASSERT_ERRNO_SUCCESS(); +} + +TEST(LlvmLibcPipe2Test, ReadAndWriteViaPipe2) { + int pipefd[2]; + int flags; + + LIBC_NAMESPACE::libc_errno = 0; + using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds; + + // Create pipe(2) with flags set to 0 + flags = 0; + ASSERT_NE(LIBC_NAMESPACE::pipe2(pipefd, flags), -1); + ASSERT_ERRNO_SUCCESS(); + + // Write something via the pipe and read from other end + constexpr char MESSAGE[] = "Hello from the write end!"; + constexpr size_t MESSAGE_SIZE = sizeof(MESSAGE); + char buf[MESSAGE_SIZE]; + ASSERT_EQ(ssize_t(MESSAGE_SIZE), + LIBC_NAMESPACE::write(pipefd[1], MESSAGE, MESSAGE_SIZE)); + ASSERT_EQ(ssize_t(MESSAGE_SIZE), + LIBC_NAMESPACE::read(pipefd[0], buf, MESSAGE_SIZE)); + ASSERT_STREQ(buf, MESSAGE); + + // Close the pipe file descriptors + ASSERT_NE(LIBC_NAMESPACE::close(pipefd[0]), -1); + ASSERT_ERRNO_SUCCESS(); + ASSERT_NE(LIBC_NAMESPACE::close(pipefd[1]), -1); + ASSERT_ERRNO_SUCCESS(); +} + +TEST(LlvmLibcPipe2Test, Pipe2InvalidFlags) { + int invalidflags = 0xDEADBEEF; + int pipefd[2]; + + using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails; + ASSERT_THAT(LIBC_NAMESPACE::pipe2(pipefd, invalidflags), Fails(EINVAL)); +} + +TEST(LlvmLibcPipe2Test, Pipe2InvalidPipeFD) { + using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails; + ASSERT_THAT(LIBC_NAMESPACE::pipe2(NULL, 0), Fails(EFAULT)); +}