3b7b998eee7e0f1e356467f285ef64a82ae152b8
[platform/core/security/vasum.git] / common / utils / fd-utils.cpp
1 /*
2 *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3 *
4 *  Contact: Jan Olszak <j.olszak@samsung.com>
5 *
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
9 *
10 *      http://www.apache.org/licenses/LICENSE-2.0
11 *
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
17 */
18
19 /**
20  * @file
21  * @author  Jan Olszak (j.olszak@samsung.com)
22  * @brief   File descriptor utility functions
23  */
24
25 #include "config.hpp"
26
27 #include "utils/fd-utils.hpp"
28 #include "utils/exception.hpp"
29 #include "logger/logger.hpp"
30
31 #include <cerrno>
32 #include <cstring>
33 #include <chrono>
34 #include <unistd.h>
35 #include <poll.h>
36 #include <sys/resource.h>
37 #include <sys/socket.h>
38 #include <boost/filesystem.hpp>
39
40 namespace fs = boost::filesystem;
41 namespace chr = std::chrono;
42
43 namespace utils {
44
45 // TODO: Add here various fixes from config::FDStore
46
47 namespace {
48
49 void waitForEvent(int fd,
50                   short event,
51                   const chr::high_resolution_clock::time_point deadline)
52 {
53     // Wait for the rest of the data
54     struct pollfd fds[1];
55     fds[0].fd = fd;
56     fds[0].events = event;
57
58     for (;;) {
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");
64         }
65
66         int ret = ::poll(fds, 1 /*fds size*/, timeoutMS.count());
67
68         if (ret == -1) {
69             if (errno == EINTR) {
70                 continue;
71             }
72             const std::string msg = "Error in poll: " + getSystemErrorMessage();
73             LOGE(msg);
74             throw UtilsException(msg);
75         }
76
77         if (ret == 0) {
78             const std::string msg = "Timeout in read";
79             LOGE(msg);
80             throw UtilsException(msg);
81         }
82
83         if (fds[0].revents & event) {
84             // Here Comes the Sun
85             break;
86         }
87
88         if (fds[0].revents & POLLHUP) {
89             const std::string msg = "Peer disconnected";
90             LOGW(msg);
91             throw UtilsException(msg);
92         }
93     }
94 }
95
96 } // namespace
97
98 void close(int fd)
99 {
100     if (fd < 0) {
101         return;
102     }
103
104     for (;;) {
105         if (-1 == ::close(fd)) {
106             if (errno == EINTR) {
107                 LOGT("Close interrupted by a signal, retrying");
108                 continue;
109             }
110             LOGE("Error in close: " << getSystemErrorMessage());
111         }
112         break;
113     }
114 }
115
116 void shutdown(int fd)
117 {
118     if (fd < 0) {
119         return;
120     }
121
122     if (-1 == ::shutdown(fd, SHUT_RDWR)) {
123         std::string msg = "shutdown() failed: " + getSystemErrorMessage();
124         LOGE(msg);
125         throw UtilsException(msg);
126     }
127 }
128
129 void write(int fd, const void* bufferPtr, const size_t size, int timeoutMS)
130 {
131     chr::high_resolution_clock::time_point deadline = chr::high_resolution_clock::now() +
132             chr::milliseconds(timeoutMS);
133
134     size_t nTotal = 0;
135     for (;;) {
136         int n  = ::write(fd,
137                          reinterpret_cast<const char*>(bufferPtr) + nTotal,
138                          size - nTotal);
139         if (n >= 0) {
140             nTotal += n;
141             if (nTotal == size) {
142                 // All data is written, break loop
143                 break;
144             }
145         } else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
146             // Neglected errors
147             LOGD("Retrying write");
148         } else {
149             const std::string msg = "Error during writing: " + getSystemErrorMessage();
150             LOGE(msg);
151             throw UtilsException(msg);
152         }
153
154         waitForEvent(fd, POLLOUT, deadline);
155     }
156 }
157
158 void read(int fd, void* bufferPtr, const size_t size, int timeoutMS)
159 {
160     chr::high_resolution_clock::time_point deadline = chr::high_resolution_clock::now() +
161             chr::milliseconds(timeoutMS);
162
163     size_t nTotal = 0;
164     for (;;) {
165         int n  = ::read(fd,
166                         reinterpret_cast<char*>(bufferPtr) + nTotal,
167                         size - nTotal);
168         if (n >= 0) {
169             nTotal += n;
170             if (nTotal == size) {
171                 // All data is read, break loop
172                 break;
173             }
174             if (n == 0) {
175                 const std::string msg = "Peer disconnected";
176                 LOGW(msg);
177                 throw UtilsException(msg);
178             }
179         } else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
180             // Neglected errors
181             LOGD("Retrying read");
182         } else {
183             const std::string msg = "Error during reading: " + getSystemErrorMessage();
184             LOGE(msg);
185             throw UtilsException(msg);
186         }
187
188         waitForEvent(fd, POLLIN, deadline);
189     }
190 }
191
192 unsigned int getMaxFDNumber()
193 {
194     struct rlimit rlim;
195     if (-1 ==  getrlimit(RLIMIT_NOFILE, &rlim)) {
196         const std::string msg = "Error during getrlimit: " + getSystemErrorMessage();
197         LOGE(msg);
198         throw UtilsException(msg);
199     }
200     return rlim.rlim_cur;
201 }
202
203 void setMaxFDNumber(unsigned int limit)
204 {
205     struct rlimit rlim;
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();
210         LOGE(msg);
211         throw UtilsException(msg);
212     }
213 }
214
215 unsigned int getFDNumber()
216 {
217     const std::string path = "/proc/self/fd/";
218     return std::distance(fs::directory_iterator(path),
219                          fs::directory_iterator());
220 }
221
222 int fdRecv(int socket, const unsigned int timeoutMS)
223 {
224     std::chrono::high_resolution_clock::time_point deadline =
225         std::chrono::high_resolution_clock::now() +
226         std::chrono::milliseconds(timeoutMS);
227
228     // Space for the file descriptor
229     union {
230         struct cmsghdr cmh;
231         char   control[CMSG_SPACE(sizeof(int))];
232     } controlUnion;
233
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;
238
239     // Setup the input buffer
240     // Ensure at least 1 byte is transmited via the socket
241     char buf;
242     struct iovec iov;
243     iov.iov_base = &buf;
244     iov.iov_len = sizeof(char);
245
246     // Set the ancillary data buffer
247     // The socket has to be connected, so we don't need to specify the name
248     struct msghdr msgh;
249     ::memset(&msgh, 0, sizeof(msgh));
250
251     msgh.msg_iov = &iov;
252     msgh.msg_iovlen = 1;
253
254     msgh.msg_control = controlUnion.control;
255     msgh.msg_controllen = sizeof(controlUnion.control);
256
257     // Receive
258     for(;;) {
259         ssize_t ret = ::recvmsg(socket, &msgh, MSG_WAITALL);
260         if (ret < 0) {
261             if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
262                 // Neglected errors, retry
263             } else {
264                 throw UtilsException("Error during recvmsg: " + getSystemErrorMessage());
265             }
266         } else if (ret == 0) {
267             throw UtilsException("Peer disconnected");
268         } else {
269             // We receive only 1 byte of data. No need to repeat
270             break;
271         }
272
273         waitForEvent(socket, POLLIN, deadline);
274     }
275
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");
284     }
285
286     return *(reinterpret_cast<int*>(CMSG_DATA(cmhp)));
287 }
288
289 bool fdSend(int socket, int fd, const unsigned int timeoutMS)
290 {
291     std::chrono::high_resolution_clock::time_point deadline =
292         std::chrono::high_resolution_clock::now() +
293         std::chrono::milliseconds(timeoutMS);
294
295     // Space for the file descriptor
296     union {
297         struct cmsghdr cmh;
298         char   control[CMSG_SPACE(sizeof(int))];
299     } controlUnion;
300
301     // Ensure at least 1 byte is transmited via the socket
302     struct iovec iov;
303     char buf = '!';
304     iov.iov_base = &buf;
305     iov.iov_len = sizeof(char);
306
307     // Fill the message to send:
308     // The socket has to be connected, so we don't need to specify the name
309     struct msghdr msgh;
310     ::memset(&msgh, 0, sizeof(msgh));
311
312     // Only iovec to transmit one element
313     msgh.msg_iov = &iov;
314     msgh.msg_iovlen = 1;
315
316     // Ancillary data buffer
317     msgh.msg_control = controlUnion.control;
318     msgh.msg_controllen = sizeof(controlUnion.control);
319
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;
327
328     // Send
329     for(;;) {
330         ssize_t ret = ::sendmsg(socket, &msgh, MSG_NOSIGNAL);
331         if (ret < 0) {
332             if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
333                 // Neglected errors, retry
334             } else {
335                 throw UtilsException("Error during sendmsg: " + getSystemErrorMessage());
336             }
337         } else if (ret == 0) {
338             // Retry the sending
339         } else {
340             // We send only 1 byte of data. No need to repeat
341             break;
342         }
343
344         waitForEvent(socket, POLLOUT, deadline);
345     }
346
347     // TODO: It shouldn't return
348     return true;
349 }
350
351 } // namespace utils
352