aboutsummaryrefslogtreecommitdiff
path: root/src/base/platform/unix/UnixFile.cpp
blob: 00ed3b9cd3ce228ea49ad6bc4048a91549aa7279 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include "cru/base/platform/unix/UnixFile.h"
#include "cru/base/Exception.h"
#include "cru/base/log/Logger.h"

#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <cerrno>

namespace cru::platform::unix {

UnixFileDescriptor::UnixFileDescriptor()
    : descriptor_(-1), auto_close_(false) {}

UnixFileDescriptor::UnixFileDescriptor(int descriptor, bool auto_close,
                                       std::function<int(int)> close)
    : descriptor_(descriptor),
      auto_close_(auto_close),
      close_(std::move(close)) {}

UnixFileDescriptor::~UnixFileDescriptor() {
  constexpr auto kLogTag = "cru::platform::unix::UnixFileDescriptor";
  if (this->IsValid() && this->IsAutoClose()) {
    if (!this->DoClose()) {
      CRU_LOG_TAG_ERROR("Failed to close file descriptor {}, errno {}.",
                        descriptor_, errno);
    }
  }
}

UnixFileDescriptor::UnixFileDescriptor(UnixFileDescriptor&& other) noexcept
    : descriptor_(other.descriptor_),
      auto_close_(other.auto_close_),
      close_(std::move(other.close_)) {
  other.descriptor_ = -1;
  other.auto_close_ = false;
  other.close_ = nullptr;
}

UnixFileDescriptor& UnixFileDescriptor::operator=(
    UnixFileDescriptor&& other) noexcept {
  if (this != &other) {
    if (this->IsValid()) {
      this->Close();
    }
    this->descriptor_ = other.descriptor_;
    this->auto_close_ = other.auto_close_;
    this->close_ = other.close_;
    other.descriptor_ = -1;
    other.auto_close_ = false;
    other.close_ = nullptr;
  }
  return *this;
}

bool UnixFileDescriptor::IsValid() const { return this->descriptor_ >= 0; }

void UnixFileDescriptor::EnsureValid() const {
  if (!this->IsValid()) {
    throw Exception("Can't do operation on an invalid unix file descriptor.");
  }
}

int UnixFileDescriptor::GetValue() const {
  EnsureValid();
  return this->descriptor_;
}

void UnixFileDescriptor::Close() {
  EnsureValid();
  if (!this->DoClose()) {
    throw ErrnoException("Failed to call close on file descriptor.");
  }
  descriptor_ = -1;
  auto_close_ = false;
}

bool UnixFileDescriptor::DoClose() {
  if (this->close_) {
    return this->close_(this->descriptor_) == 0;
  } else {
    return ::close(this->descriptor_) == 0;
  }
}

ssize_t UnixFileDescriptor::Read(void* buffer, size_t size) {
  EnsureValid();
  auto result = ::read(GetValue(), buffer, size);
  if (result == -1) {
    if (errno == EAGAIN || errno == EWOULDBLOCK) {
      return -1;
    } else {
      throw ErrnoException("Failed to read on file descriptor.");
    }
  }
  return result;
}

ssize_t UnixFileDescriptor::Write(const void* buffer, size_t size) {
  EnsureValid();
  auto result = ::write(GetValue(), buffer, size);
  if (result == -1) {
    if (errno == EAGAIN || errno == EWOULDBLOCK) {
      return -1;
    } else {
      throw ErrnoException("Failed to write on file descriptor.");
    }
  }
  return result;
}

void UnixFileDescriptor::SetFileDescriptorFlags(int flags) {
  EnsureValid();
  if (::fcntl(GetValue(), F_SETFL, flags) == -1) {
    throw ErrnoException("Failed to set flags on file descriptor.");
  }
}

UniDirectionalUnixPipeResult OpenUniDirectionalPipe(UnixPipeFlag flags) {
  int fds[2];
  if (::pipe(fds) != 0) {
    throw ErrnoException("Failed to create unix pipe.");
  }

  UnixFileDescriptor read(fds[0]), write(fds[1]);

  if (flags & UnixPipeFlags::NonBlock) {
    read.SetFileDescriptorFlags(O_NONBLOCK);
    write.SetFileDescriptorFlags(O_NONBLOCK);
  }

  return {std::move(read), std::move(write)};
}
}  // namespace cru::platform::unix