aboutsummaryrefslogtreecommitdiffstats
path: root/liblfjail/fd.cc
blob: 058a62dc0fd8e587cefb6a7c6fc892b2f1f914e4 (plain) (blame)
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
/*
 * This source code is released into the public domain.
 */

#include "fd.hh"

namespace lfjail {

fd::fd(int fd_) noexcept
	: _fd(fd_)
{}

fd::fd(fd &&other) noexcept
	: _fd(std::exchange(other._fd, _invalid_fd))
{}

auto fd::operator=(this fd &self, fd &&other) noexcept -> fd & {
	if (&self != &other)
		self._fd = std::exchange(other._fd, _invalid_fd);
	return self;
}

fd::operator bool(this fd const &self) noexcept {
	return self._fd != self._invalid_fd;
}

fd::~fd() {
	if (*this)
		this->close();
}

auto fd::close(this fd &self) -> void {
	(void)::close(self.get());
	self._fd = _invalid_fd;
}

auto fd::get(this fd const &self) -> int {
	if (!self)
		std::abort();
	return self._fd;
}

auto fd::release(this fd &&self) -> int {
	if (!self)
		std::abort();
	return std::exchange(self._fd, _invalid_fd);
}

// Create a copy of this fd by calling dup().
auto dup(fd const &self) -> fd {
	auto thisfd = self.get();

	auto const newfd = ::dup(thisfd);
	if (newfd == -1)
		throw generic_error("dup({}): {}", thisfd,
				    std::strerror(errno));

	return {newfd};
}

// Create a copy of this fd by calling dup2().  The second argument
// must not be managed by an instance of the fd class, or else it
// will be closed twice.
auto dup(fd const &self, int newfd) -> fd {
	auto thisfd = self.get();

	// dup2() allows the existing and new fd to be the same,
	// but this is error-prone and usually a logic error, so
	// we reject it.
	if (thisfd == newfd)
		std::abort();

	auto const ret = ::dup2(thisfd, newfd);
	if (ret == -1)
		throw generic_error("dup({}): {}", thisfd,
				    std::strerror(errno));

	return {newfd};
}

// Return the fnctl flags for this fd.
auto getflags(fd const &self) -> int {
	auto const flags = ::fcntl(self.get(), F_GETFL);

	if (flags == -1)
		throw generic_error("fcntl({}, F_GETFL): {}",
				    self.get(), strerror(errno));

	return flags;
}

// Replace the fnctl flags for this fd.
auto replaceflags(fd &self, int newflags) -> void {
	auto const ret = ::fcntl(self.get(), F_SETFL, newflags);

	if (ret == -1)
		throw generic_error("fcntl({}, F_SETFL, {}): {}",
				    self.get(), newflags, strerror(errno));
}

// Add bits to the fcntl flags for this fd.
auto setflags(fd &self, int newflags) -> void {
	auto flags = getflags(self);
	flags |= newflags;
	replaceflags(self, flags);
}

// Remove bits from the fcntl flags for this fd.
auto clearflags(fd &self, int clrflags) -> void {
	auto flags = getflags(self);
	flags &= ~clrflags;
	replaceflags(self, flags);
}

// Create two fds by calling pipe() and return them.
auto pipe() -> std::pair<fd, fd> {
	auto fds = std::array<int, 2>{};

	if (auto const ret = ::pipe(fds.data()); ret != 0)
		throw generic_error("pipe(): {}", std::strerror(errno));

	return {fd(fds[0]), fd(fds[1])};
}

} // namespace lfjail