GCC Code Coverage Report


Directory: ./
File: pdcom5/src/PosixProcess.cpp
Date: 2024-12-15 04:08:34
Exec Total Coverage
Lines: 86 124 69.4%
Branches: 29 103 28.2%

Line Branch Exec Source
1 /*****************************************************************************
2 * vim:tw=78
3 *
4 * Copyright (C) 2021 Bjarne von Horn (vh at igh dot de).
5 *
6 * This file is part of the PdCom library.
7 *
8 * The PdCom library is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * The PdCom library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16 * License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with the PdCom library. If not, see <http://www.gnu.org/licenses/>.
20 *
21 *****************************************************************************/
22
23 #include <algorithm>
24 #include <arpa/inet.h>
25 #include <array>
26 #include <cstring>
27 #include <errno.h>
28 #include <memory>
29 #include <netdb.h>
30 #include <netinet/in.h>
31 #include <pdcom5/Exception.h>
32 #include <pdcom5/PosixProcess.h>
33 #include <sys/select.h>
34 #include <sys/socket.h>
35 #include <sys/types.h>
36 #include <unistd.h>
37
38 using PdCom::PosixProcess;
39
40 namespace {
41 template <typename T>
42 78 bool do_connect(int &fd, const T &addr, int af, socklen_t len = sizeof(T))
43 {
44 static_assert(!std::is_pointer<T>::value, "addr must be by reference");
45 78 fd = socket(af, SOCK_STREAM, 0);
46
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 78 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
78 if (fd == -1)
47 return false;
48
2/6
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 39 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
78 if (::connect(fd, reinterpret_cast<const sockaddr *>(&addr), len) >= 0)
49 39 return true;
50 39 ::close(fd);
51 39 fd = -1;
52 39 return false;
53 }
54
55 2650 void writeToSocket(
56 const char *begin,
57 const char *const end,
58 const int fd,
59 const timeval &ts)
60 {
61 using PdCom::WriteFailure;
62
2/2
✓ Branch 0 taken 1325 times.
✓ Branch 1 taken 1325 times.
3975 while (begin != end) {
63 1325 const auto res = ::write(fd, begin, end - begin);
64
1/2
✓ Branch 0 taken 1325 times.
✗ Branch 1 not taken.
1325 if (res > 0) {
65 1325 begin += res;
66 }
67 else if (res <= 0) {
68 if (errno != EAGAIN)
69 throw WriteFailure(errno);
70
71 if (fd < 0 || fd >= FD_SETSIZE)
72 throw WriteFailure("fd >= FD_SETSIZE");
73 timeval timeout = ts;
74 fd_set fds;
75 FD_ZERO(&fds);
76 FD_SET(fd, &fds);
77 switch (::select(fd + 1, nullptr, &fds, nullptr, &timeout)) {
78 case 1:
79 break;
80 case 0:
81 throw WriteFailure(ETIMEDOUT);
82 default:
83 throw WriteFailure(errno);
84 }
85 }
86 }
87 1325 }
88 } // namespace
89
90 39 class PDCOM5_LOCAL PdCom::PosixProcess::Impl
91 {
92 public:
93 39 class PDCOM5_LOCAL Buffer
94 {
95 std::array<char, 1024> buffer_ = {};
96
97 size_t size_ = 0;
98
99 public:
100 ::timeval timeout = {1, 0};
101
102 1985 size_t size() const { return size_; }
103 1985 size_t capacity() const { return buffer_.size(); }
104
105 1985 bool append(const char *buf, size_t count)
106 {
107
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 1985 times.
1985 if (count > (capacity() - size()))
108 return false;
109 1985 std::copy(buf, buf + count, buffer_.data() + size_);
110 1985 size_ += count;
111 1985 return true;
112 }
113
114 79 void flush(const int fd)
115 {
116 79 writeToSocket(buffer_.data(), buffer_.data() + size_, fd, timeout);
117 79 size_ = 0;
118 79 }
119
120 39 void clear() { size_ = 0; }
121 } buffer_;
122 };
123
124 39 PosixProcess::PosixProcess(const char *host, unsigned short port) :
125 39 fd_(-1), impl_(std::make_shared<Impl>())
126 {
127
1/2
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
39 reconnect(host, port);
128 39 }
129
130 39 void PosixProcess::reconnect(const char *host, unsigned short port)
131 {
132 78 const auto connect_v4 = [&]() {
133 39 sockaddr_in server_addr;
134 39 ::memset(&server_addr, 0, sizeof(server_addr));
135 39 server_addr.sin_family = AF_INET;
136 39 server_addr.sin_port = htons(port);
137
1/2
✓ Branch 3 taken 39 times.
✗ Branch 4 not taken.
39 if (inet_pton(AF_INET, host, &server_addr.sin_addr) <= 0)
138 39 return false;
139 return do_connect(fd_, server_addr, AF_INET);
140 39 };
141
142 78 const auto connect_v6 = [&]() {
143 39 sockaddr_in6 server_addr;
144 39 ::memset(&server_addr, 0, sizeof(server_addr));
145 39 server_addr.sin6_family = AF_INET6;
146 39 server_addr.sin6_port = htons(port);
147
1/2
✓ Branch 3 taken 39 times.
✗ Branch 4 not taken.
39 if (inet_pton(AF_INET6, host, &server_addr.sin6_addr) <= 0)
148 39 return false;
149 return do_connect(fd_, server_addr, AF_INET6);
150 39 };
151
152 78 const auto connect_dns = [&]() {
153
1/2
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
78 const auto port_s = std::to_string(port);
154 39 const ::addrinfo hints {
155 AI_NUMERICSERV | AI_V4MAPPED | AI_ADDRCONFIG,
156 AF_UNSPEC,
157 SOCK_STREAM,
158 0,
159 0,
160 nullptr,
161 nullptr,
162 nullptr};
163 struct MyAddrInfo
164 {
165 ::addrinfo *res = nullptr;
166 39 ~MyAddrInfo()
167 39 {
168
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 if (res)
169 39 freeaddrinfo(res);
170 39 }
171 78 } res;
172
2/4
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 39 times.
39 if (::getaddrinfo(host, port_s.c_str(), &hints, &res.res) != 0)
173 return false;
174 39 ::addrinfo *i = res.res;
175
1/2
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
117 while (i) {
176
3/4
✓ Branch 7 taken 78 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 39 times.
✓ Branch 10 taken 39 times.
78 if (do_connect(fd_, *(i->ai_addr), i->ai_family, i->ai_addrlen))
177 39 return true;
178 39 i = i->ai_next;
179 }
180 return false;
181 39 };
182
183
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 39 times.
39 if (fd_ != -1)
184 close(fd_);
185 39 impl_->buffer_.clear();
186
7/14
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 39 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 39 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 39 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 39 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 39 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 39 times.
39 if (!connect_v4() and !connect_v6() and !connect_dns()) {
187 throw ConnectionFailed();
188 }
189 39 }
190
191 PosixProcess::PosixProcess(int fd) : fd_(fd), impl_(std::make_shared<Impl>())
192 {}
193
194 78 PosixProcess::~PosixProcess()
195 {
196
1/2
✓ Branch 3 taken 39 times.
✗ Branch 4 not taken.
39 if (fd_ >= 0)
197 39 close(fd_);
198 39 }
199
200
201 79 void PosixProcess::posixFlush()
202 {
203 79 impl_->buffer_.flush(fd_);
204 79 }
205
206 1985 void PosixProcess::posixWriteBuffered(const char *buf, size_t count)
207 {
208
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 1985 times.
1985 if (!impl_->buffer_.append(buf, count)) {
209 impl_->buffer_.flush(fd_);
210 posixWriteDirect(buf, count);
211 }
212 1985 }
213
214 1246 void PosixProcess::posixWriteDirect(const char *buf, size_t count)
215 {
216 1246 writeToSocket(buf, buf + count, fd_, impl_->buffer_.timeout);
217 1246 }
218
219 394 int PosixProcess::posixRead(char *buf, int count)
220 {
221 394 const auto res = ::read(fd_, buf, count);
222
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 394 times.
394 if (res < 0) {
223 if (errno != EAGAIN && errno != EINTR)
224 throw ReadFailure(errno);
225 int e = errno;
226 errno = 0;
227 return -e;
228 }
229 394 return res;
230 }
231
232 void PosixProcess::setWriteTimeout(std::chrono::milliseconds ms)
233 {
234 const auto seconds = std::chrono::duration_cast<std::chrono::seconds>(ms);
235 const auto us =
236 std::chrono::duration_cast<std::chrono::microseconds>(ms - seconds);
237
238 impl_->buffer_.timeout.tv_sec = seconds.count();
239 impl_->buffer_.timeout.tv_usec = us.count();
240 }
241