97 lines
3.4 KiB
C++
97 lines
3.4 KiB
C++
/* ************************************************************************** */
|
|
/* */
|
|
/* ::: :::::::: */
|
|
/* FileDescriptor.cpp :+: :+: :+: */
|
|
/* +:+ +:+ +:+ */
|
|
/* By: acossari <acossari@student.codam.nl> +#+ +:+ +#+ */
|
|
/* +#+#+#+#+#+ +#+ */
|
|
/* Created: 2026/08/24 16:23:52 by acossari #+# #+# */
|
|
/* Updated: 2026/08/28 16:13:23 by acossari ### ########.fr */
|
|
/* */
|
|
/* ************************************************************************** */
|
|
|
|
#include "net/FileDescriptor.hpp"
|
|
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
#include <cerrno>
|
|
#include <cstring>
|
|
#include <format>
|
|
#include <utility>
|
|
|
|
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
|