Skip to content

Commit

Permalink
mil_tools/os/FileDescriptor.cpp: Add file descriptor class for file/p…
Browse files Browse the repository at this point in the history
…ty/pipes
  • Loading branch information
cbrxyz committed Jan 6, 2025
1 parent 3de1d09 commit 06dda9e
Show file tree
Hide file tree
Showing 7 changed files with 271 additions and 2 deletions.
13 changes: 11 additions & 2 deletions src/mil_common/mil_tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ if(NOT CMAKE_CXX_STANDARD)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
add_compile_options(-Wall -Wextra -Werror)
endif()

find_package(backward_ros REQUIRED)
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)

add_library(${PROJECT_NAME}
src/string.cpp
src/fs/path.cpp
src/os.cpp
src/os/FileDescriptor.cpp
src/string.cpp
)
include_directories(include)
ament_target_dependencies(${PROJECT_NAME} rclcpp)
Expand Down Expand Up @@ -51,4 +53,11 @@ if(BUILD_TESTING)
$<INSTALL_INTERFACE:include>
)
target_link_libraries(${PROJECT_NAME}_string_test ${PROJECT_NAME})

ament_add_gtest(${PROJECT_NAME}_os_test test/os.cpp)
target_include_directories(${PROJECT_NAME}_os_test PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(${PROJECT_NAME}_os_test ${PROJECT_NAME})
endif()
31 changes: 31 additions & 0 deletions src/mil_common/mil_tools/include/mil_tools/os.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* os.hpp - High-level OS utilities
*/
#pragma once

#include <fcntl.h>
#include <pty.h>
#include <string.h>
#include <unistd.h>

#include <stdexcept>
#include <string>
#include <tuple>
#include <vector>

#include "mil_tools/os/FileDescriptor.hpp"

namespace mil_tools::os
{
/// Opens a file
FileDescriptor open(std::string const& filename, int flags);
/// Open a new pseudo-terminal pair (master, slave)
std::tuple<FileDescriptor, FileDescriptor> openpty();
/// Closes a file descriptor
void close(int fd);
/// Reads a certain number of bytes from a file descriptor
std::vector<char> read(int fd, size_t count);
/// Writes a string to a file descriptor
void write(int fd, std::vector<char> const& data);
void write(int fd, std::string const& data);
}; // namespace mil_tools::os
45 changes: 45 additions & 0 deletions src/mil_common/mil_tools/include/mil_tools/os/FileDescriptor.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

#include <string.h>

#include <stdexcept>
#include <string>
#include <tuple>
#include <vector>

namespace mil_tools::os
{
class FileDescriptor
{
private:
int fd_;

public:
FileDescriptor() : fd_(-1)
{
}
explicit FileDescriptor(int fd) : fd_(fd)
{
}
~FileDescriptor();
inline int get() const
{
return fd_;
};
inline bool valid() const
{
return fd_ >= 0;
};
explicit operator int() const;
FileDescriptor(FileDescriptor const&) = delete;
FileDescriptor& operator=(FileDescriptor const&) = delete;
FileDescriptor(FileDescriptor&& other);
FileDescriptor& operator=(FileDescriptor&& other);
void close();
std::vector<char> read(int size);
std::string read_as_string(int size);
void write(std::string const& data);
void write(std::vector<char> const& data);
};

} // namespace mil_tools::os
Empty file.
73 changes: 73 additions & 0 deletions src/mil_common/mil_tools/src/os.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include "mil_tools/os.hpp"

#include "mil_tools/os/FileDescriptor.hpp"

namespace mil_tools::os
{
std::tuple<FileDescriptor, FileDescriptor> openpty()
{
int master = -1, slave = -1;
if (::openpty(&master, &slave, nullptr, nullptr, nullptr) == -1)
{
throw std::runtime_error("openpty failed: " + std::string(strerror(errno)));
}
if (grantpt(master) < 0)
{
throw std::runtime_error("grantpt failed: " + std::string(strerror(errno)));
}
if (unlockpt(master) < 0)
{
throw std::runtime_error("unlockpt failed: " + std::string(strerror(errno)));
}
return { FileDescriptor(master), FileDescriptor(slave) };
}

void close(int fd)
{
if (::close(fd) == -1)
{
throw std::runtime_error("close failed: " + std::string(strerror(errno)));
}
}

std::vector<char> read(int fd, size_t count)
{
std::vector<char> data(count);
ssize_t n = ::read(fd, data.data(), count);
if (n == -1)
{
throw std::runtime_error("read failed: " + std::string(strerror(errno)));
}
data.resize(n);
return data;
}

void write(int fd, std::vector<char> const& data)
{
ssize_t total_written = 0;
while (total_written < static_cast<ssize_t>(data.size()))
{
ssize_t n = ::write(fd, data.data() + total_written, data.size() - total_written);
if (n == -1)
{
throw std::runtime_error("write failed: " + std::string(strerror(errno)));
}
total_written += n;
}
}

void write(int fd, std::string const& data)
{
write(fd, std::vector<char>(data.begin(), data.end()));
}

FileDescriptor open(std::string const& filename, int flags)
{
auto fd = ::open(filename.c_str(), flags);
if (fd < 0)
{
throw std::runtime_error("open failed: " + std::string(strerror(errno)));
}
return FileDescriptor(fd);
}
} // namespace mil_tools::os
63 changes: 63 additions & 0 deletions src/mil_common/mil_tools/src/os/FileDescriptor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include "mil_tools/os/FileDescriptor.hpp"

#include "mil_tools/os.hpp"

namespace mil_tools::os
{
FileDescriptor::~FileDescriptor()
{
close();
}

FileDescriptor::operator int() const
{
return fd_;
};

FileDescriptor::FileDescriptor(FileDescriptor&& other)
{
fd_ = other.fd_;
other.fd_ = -1;
}

FileDescriptor& FileDescriptor::operator=(FileDescriptor&& other)
{
if (this != &other)
{
fd_ = other.fd_;
other.fd_ = -1;
}
return *this;
}

void FileDescriptor::close()
{
if (fd_ != -1)
{
mil_tools::os::close(fd_);
fd_ = -1;
}
}

std::vector<char> FileDescriptor::read(int size)
{
return mil_tools::os::read(fd_, size);
}

std::string FileDescriptor::read_as_string(int size)
{
std::vector<char> raw = read(size);
return std::string(raw.begin(), raw.end());
}

void FileDescriptor::write(std::vector<char> const& data)
{
mil_tools::os::write(fd_, data);
}

void FileDescriptor::write(std::string const& data)
{
mil_tools::os::write(fd_, data);
}

} // namespace mil_tools::os
48 changes: 48 additions & 0 deletions src/mil_common/mil_tools/test/os.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include "mil_tools/os.hpp"

#include <gtest/gtest.h>

#include <cstdio>
#include <fstream>
#include <iostream>

#include "mil_tools/fs/path.hpp"

TEST(mil_tools_os, open)
{
auto fd_null = mil_tools::os::open("/dev/null", O_RDWR);
EXPECT_TRUE(fd_null.valid());
fd_null.close();
EXPECT_FALSE(fd_null.valid());
auto fd_readme = mil_tools::os::open(mil_tools::fs::path::expanduser("~/mil2/README.md"), O_RDONLY);
EXPECT_EQ(fd_readme.read_as_string(5), "![Col");
}

TEST(mil_tools_os, write)
{
// create file if not eist
std::ofstream(mil_tools::fs::path::expanduser("~/mil2/tmp.txt"));

// write with fd
std::string filename = mil_tools::fs::path::expanduser("~/mil2/tmp.txt");
auto fd = mil_tools::os::open(filename, O_RDWR | O_CREAT);
mil_tools::os::write(fd.get(), "hello");
fd.write(" world");
fd.close();

// check contents
std::ifstream file(filename);
std::string line;
std::getline(file, line);
EXPECT_EQ(line, "hello world");
file.close();

// delete file
std::remove(filename.c_str());
}

int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

0 comments on commit 06dda9e

Please sign in to comment.