/* ************************************************************************** */ /* */ /* ::: :::::::: */ /* FileDescriptor.cpp :+: :+: :+: */ /* +:+ +:+ +:+ */ /* By: acossari +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/08/24 16:23:52 by acossari #+# #+# */ /* Updated: 2026/08/28 16:13:23 by acossari ### ########.fr */ /* */ /* ************************************************************************** */ #include "net/FileDescriptor.hpp" #include #include #include #include #include #include namespace webserv { // "Sink parameter": consumes the local parameter by moving its value into msg_ // lvalue -> copy into message -> move into msg_ (1 copy, worst case scenario) // rvalue -> move into message -> move into msg_ (0 copies) FileDescriptor::Error::Error(std::string message) : msg_(std::move(message)) {} const char* FileDescriptor::Error::what() const noexcept { return msg_.c_str(); } // A valid fd is either made close-on-exec or closed before construction fails. FileDescriptor::FileDescriptor(int fd) : fd_(fd) { if (fd_ < 0) { return; } // fork() inherits this fd, but a successful execve() closes it before the // new program starts, preventing CGI processes from retaining server fds. if (::fcntl(fd_, F_SETFD, FD_CLOEXEC) == -1) { // The subject forbids errno only after read/recv/write/send. // Save it before close(), which may overwrite the original fcntl error. const int errnoCode = errno; ::close(fd_); fd_ = -1; throw Error(std::format("fcntl(F_SETFD, FD_CLOEXEC) on fd {}: {}", fd, std::strerror(errnoCode))); } } FileDescriptor::~FileDescriptor() { reset(); } // The FileDescriptor&& parameter is an rvalue reference and a sink. It accepts // only a temporary or an object passed with std::move(). The constructor takes // ownership of that object's fd, so only the new FileDescriptor will close it. FileDescriptor::FileDescriptor(FileDescriptor&& other) noexcept : fd_(std::exchange(other.fd_, -1)) {} FileDescriptor& FileDescriptor::operator=(FileDescriptor&& other) noexcept { if (this != &other) { reset(); fd_ = std::exchange(other.fd_, -1); } return *this; } void FileDescriptor::setNonBlocking() { if (!valid()) { throw Error("setNonBlocking() on an invalid file descriptor"); } // Read, modify, write pattern: F_SETFL replaces the whole status flags word, // so passing O_NONBLOCK alone would clear whatever else is set. const int flags = ::fcntl(fd_, F_GETFL); if (flags == -1) { throw Error( std::format("fcntl(F_GETFL) on fd {}: {}", fd_, std::strerror(errno))); } if (::fcntl(fd_, F_SETFL, flags | O_NONBLOCK) == -1) { throw Error(std::format("fcntl(F_SETFL, O_NONBLOCK) on fd {}: {}", fd_, std::strerror(errno))); } } void FileDescriptor::reset() noexcept { if (fd_ >= 0) { ::close(fd_); fd_ = -1; } } } // namespace webserv