2 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
4 * Contact: Jan Olszak <j.olszak@samsung.com>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License
21 * @author Jan Olszak (j.olszak@samsung.com)
22 * @brief File descriptor utility functions
27 #include "utils/fd-utils.hpp"
28 #include "utils/exception.hpp"
29 #include "logger/logger.hpp"
36 #include <sys/resource.h>
37 #include <sys/socket.h>
38 #include <boost/filesystem.hpp>
40 namespace fs = boost::filesystem;
41 namespace chr = std::chrono;
45 // TODO: Add here various fixes from config::FDStore
49 void waitForEvent(int fd,
51 const chr::high_resolution_clock::time_point deadline)
53 // Wait for the rest of the data
56 fds[0].events = event;
59 chr::milliseconds timeoutMS = chr::duration_cast<chr::milliseconds>(deadline - chr::high_resolution_clock::now());
60 if (timeoutMS.count() < 0) {
61 LOGE("Timeout while waiting for event: " << std::hex << event <<
62 " on fd: " << std::dec << fd);
63 throw UtilsException("Timeout");
66 int ret = ::poll(fds, 1 /*fds size*/, timeoutMS.count());
72 const std::string msg = "Error in poll: " + getSystemErrorMessage();
74 throw UtilsException(msg);
78 const std::string msg = "Timeout in read";
80 throw UtilsException(msg);
83 if (fds[0].revents & event) {
88 if (fds[0].revents & POLLHUP) {
89 const std::string msg = "Peer disconnected";
91 throw UtilsException(msg);
105 if (-1 == ::close(fd)) {
106 if (errno == EINTR) {
107 LOGT("Close interrupted by a signal, retrying");
110 LOGE("Error in close: " << getSystemErrorMessage());
116 void shutdown(int fd)
122 if (-1 == ::shutdown(fd, SHUT_RDWR)) {
123 std::string msg = "shutdown() failed: " + getSystemErrorMessage();
125 throw UtilsException(msg);
129 void write(int fd, const void* bufferPtr, const size_t size, int timeoutMS)
131 chr::high_resolution_clock::time_point deadline = chr::high_resolution_clock::now() +
132 chr::milliseconds(timeoutMS);
137 reinterpret_cast<const char*>(bufferPtr) + nTotal,
141 if (nTotal == size) {
142 // All data is written, break loop
145 } else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
147 LOGD("Retrying write");
149 const std::string msg = "Error during writing: " + getSystemErrorMessage();
151 throw UtilsException(msg);
154 waitForEvent(fd, POLLOUT, deadline);
158 void read(int fd, void* bufferPtr, const size_t size, int timeoutMS)
160 chr::high_resolution_clock::time_point deadline = chr::high_resolution_clock::now() +
161 chr::milliseconds(timeoutMS);
166 reinterpret_cast<char*>(bufferPtr) + nTotal,
170 if (nTotal == size) {
171 // All data is read, break loop
175 const std::string msg = "Peer disconnected";
177 throw UtilsException(msg);
179 } else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
181 LOGD("Retrying read");
183 const std::string msg = "Error during reading: " + getSystemErrorMessage();
185 throw UtilsException(msg);
188 waitForEvent(fd, POLLIN, deadline);
192 unsigned int getMaxFDNumber()
195 if (-1 == getrlimit(RLIMIT_NOFILE, &rlim)) {
196 const std::string msg = "Error during getrlimit: " + getSystemErrorMessage();
198 throw UtilsException(msg);
200 return rlim.rlim_cur;
203 void setMaxFDNumber(unsigned int limit)
206 rlim.rlim_cur = limit;
207 rlim.rlim_max = limit;
208 if (-1 == setrlimit(RLIMIT_NOFILE, &rlim)) {
209 const std::string msg = "Error during setrlimit: " + getSystemErrorMessage();
211 throw UtilsException(msg);
215 unsigned int getFDNumber()
217 const std::string path = "/proc/self/fd/";
218 return std::distance(fs::directory_iterator(path),
219 fs::directory_iterator());
222 int fdRecv(int socket, const unsigned int timeoutMS)
224 std::chrono::high_resolution_clock::time_point deadline =
225 std::chrono::high_resolution_clock::now() +
226 std::chrono::milliseconds(timeoutMS);
228 // Space for the file descriptor
231 char control[CMSG_SPACE(sizeof(int))];
234 // Describe the data that we want to recive
235 controlUnion.cmh.cmsg_len = CMSG_LEN(sizeof(int));
236 controlUnion.cmh.cmsg_level = SOL_SOCKET;
237 controlUnion.cmh.cmsg_type = SCM_RIGHTS;
239 // Setup the input buffer
240 // Ensure at least 1 byte is transmited via the socket
244 iov.iov_len = sizeof(char);
246 // Set the ancillary data buffer
247 // The socket has to be connected, so we don't need to specify the name
249 ::memset(&msgh, 0, sizeof(msgh));
254 msgh.msg_control = controlUnion.control;
255 msgh.msg_controllen = sizeof(controlUnion.control);
259 ssize_t ret = ::recvmsg(socket, &msgh, MSG_WAITALL);
261 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
262 // Neglected errors, retry
264 throw UtilsException("Error during recvmsg: " + getSystemErrorMessage());
266 } else if (ret == 0) {
267 throw UtilsException("Peer disconnected");
269 // We receive only 1 byte of data. No need to repeat
273 waitForEvent(socket, POLLIN, deadline);
276 struct cmsghdr *cmhp;
277 cmhp = CMSG_FIRSTHDR(&msgh);
278 if (cmhp == NULL || cmhp->cmsg_len != CMSG_LEN(sizeof(int))) {
279 throw UtilsException("Bad cmsg length");
280 } else if (cmhp->cmsg_level != SOL_SOCKET) {
281 throw UtilsException("cmsg_level != SOL_SOCKET");
282 } else if (cmhp->cmsg_type != SCM_RIGHTS) {
283 throw UtilsException("cmsg_type != SCM_RIGHTS");
286 return *(reinterpret_cast<int*>(CMSG_DATA(cmhp)));
289 bool fdSend(int socket, int fd, const unsigned int timeoutMS)
291 std::chrono::high_resolution_clock::time_point deadline =
292 std::chrono::high_resolution_clock::now() +
293 std::chrono::milliseconds(timeoutMS);
295 // Space for the file descriptor
298 char control[CMSG_SPACE(sizeof(int))];
301 // Ensure at least 1 byte is transmited via the socket
305 iov.iov_len = sizeof(char);
307 // Fill the message to send:
308 // The socket has to be connected, so we don't need to specify the name
310 ::memset(&msgh, 0, sizeof(msgh));
312 // Only iovec to transmit one element
316 // Ancillary data buffer
317 msgh.msg_control = controlUnion.control;
318 msgh.msg_controllen = sizeof(controlUnion.control);
320 // Describe the data that we want to send
321 struct cmsghdr *cmhp;
322 cmhp = CMSG_FIRSTHDR(&msgh);
323 cmhp->cmsg_len = CMSG_LEN(sizeof(int));
324 cmhp->cmsg_level = SOL_SOCKET;
325 cmhp->cmsg_type = SCM_RIGHTS;
326 *(reinterpret_cast<int*>(CMSG_DATA(cmhp))) = fd;
330 ssize_t ret = ::sendmsg(socket, &msgh, MSG_NOSIGNAL);
332 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
333 // Neglected errors, retry
335 throw UtilsException("Error during sendmsg: " + getSystemErrorMessage());
337 } else if (ret == 0) {
340 // We send only 1 byte of data. No need to repeat
344 waitForEvent(socket, POLLOUT, deadline);
347 // TODO: It shouldn't return