IPC unit tests and testing framework improvements
[platform/core/security/vasum.git] / libs / ipc / internals / socket.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   Linux socket wrapper
23  */
24
25 #include "config.hpp"
26
27 #include "ipc/exception.hpp"
28 #include "ipc/internals/socket.hpp"
29 #include "utils/fd-utils.hpp"
30 #include "utils/exception.hpp"
31 #include "logger/logger.hpp"
32
33 #ifdef HAVE_SYSTEMD
34 #include <systemd/sd-daemon.h>
35 #endif // HAVE_SYSTEMD
36 #include <sys/socket.h>
37 #include <sys/stat.h>
38 #include <sys/un.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <cerrno>
42 #include <cstring>
43
44 using namespace utils;
45
46 namespace ipc {
47
48 namespace {
49 const int MAX_QUEUE_LENGTH = 1000;
50
51 void setFdOptions(int fd)
52 {
53     // Prevent from inheriting fd by zones
54     if (-1 == ::fcntl(fd, F_SETFD, FD_CLOEXEC)) {
55         const std::string msg = "Error in fcntl: " + getSystemErrorMessage();
56         LOGE(msg);
57         throw IPCException(msg);
58     }
59 }
60
61 } // namespace
62
63 Socket::Socket(int socketFD)
64     : mFD(socketFD)
65 {
66 }
67
68 Socket::Socket(Socket&& socket) noexcept
69     : mFD(socket.mFD)
70 {
71     socket.mFD = -1;
72 }
73
74 Socket::~Socket() noexcept
75 {
76     try {
77         utils::close(mFD);
78     } catch (std::exception& e) {
79         LOGE("Error in Socket's destructor: " << e.what());
80     }
81 }
82
83 Socket::Guard Socket::getGuard() const
84 {
85     return Guard(mCommunicationMutex);
86 }
87
88 int Socket::getFD() const
89 {
90     return mFD;
91 }
92
93 std::shared_ptr<Socket> Socket::accept()
94 {
95     int sockfd = ::accept(mFD, nullptr, nullptr);
96     if (sockfd == -1) {
97         const std::string msg = "Error in accept: " + getSystemErrorMessage();
98         LOGE(msg);
99         throw IPCException(msg);
100     }
101     setFdOptions(sockfd);
102     return std::make_shared<Socket>(sockfd);
103 }
104
105 void Socket::write(const void* bufferPtr, const size_t size) const
106 {
107     Guard guard(mCommunicationMutex);
108     utils::write(mFD, bufferPtr, size);
109 }
110
111 void Socket::read(void* bufferPtr, const size_t size) const
112 {
113     Guard guard(mCommunicationMutex);
114     utils::read(mFD, bufferPtr, size);
115 }
116
117 #ifdef HAVE_SYSTEMD
118 int Socket::getSystemdSocketInternal(const std::string& path)
119 {
120     int n = ::sd_listen_fds(-1 /*Block further calls to sd_listen_fds*/);
121     if (n < 0) {
122         const std::string msg = "sd_listen_fds failed: " + getSystemErrorMessage(-n);
123         LOGE(msg);
124         throw IPCException(msg);
125     }
126
127     for (int fd = SD_LISTEN_FDS_START;
128             fd < SD_LISTEN_FDS_START + n;
129             ++fd) {
130         if (0 < ::sd_is_socket_unix(fd, SOCK_STREAM, 1, path.c_str(), 0)) {
131             setFdOptions(fd);
132             return fd;
133         }
134     }
135     LOGW("No usable sockets were passed by systemd.");
136     return -1;
137 }
138 #endif // HAVE_SYSTEMD
139
140 int Socket::createSocketInternal(const std::string& path)
141 {
142     // Isn't the path too long?
143     if (path.size() >= sizeof(sockaddr_un::sun_path)) {
144         const std::string msg = "Socket's path too long";
145         LOGE(msg);
146         throw IPCException(msg);
147     }
148
149     int sockfd = ::socket(AF_UNIX, SOCK_STREAM, 0);
150     if (sockfd == -1) {
151         const std::string msg = "Error in socket: " + getSystemErrorMessage();
152         LOGE(msg);
153         throw IPCSocketException(errno, msg);
154     }
155     setFdOptions(sockfd);
156
157     ::sockaddr_un serverAddress;
158     serverAddress.sun_family = AF_UNIX;
159     ::strncpy(serverAddress.sun_path, path.c_str(), sizeof(sockaddr_un::sun_path));
160     unlink(serverAddress.sun_path);
161
162     // Everybody can access the socket
163     // TODO: Use SMACK to guard the socket
164     if (-1 == ::bind(sockfd,
165                      reinterpret_cast<struct sockaddr*>(&serverAddress),
166                      sizeof(struct sockaddr_un))) {
167         utils::close(sockfd);
168         const std::string msg = "Error in bind: " + getSystemErrorMessage();
169         LOGE(msg);
170         throw IPCException(msg);
171     }
172
173     if (-1 == ::listen(sockfd,
174                        MAX_QUEUE_LENGTH)) {
175         utils::close(sockfd);
176         const std::string msg = "Error in listen: " + getSystemErrorMessage();
177         LOGE(msg);
178         throw IPCException(msg);
179     }
180
181     return sockfd;
182 }
183
184 Socket Socket::createSocket(const std::string& path)
185 {
186     // Initialize a socket
187     int fd;
188 #ifdef HAVE_SYSTEMD
189     fd = getSystemdSocketInternal(path);
190     if (fd == -1) {
191        fd = createSocketInternal(path);
192     }
193 #else
194     fd = createSocketInternal(path);
195 #endif // HAVE_SYSTEMD
196
197     return Socket(fd);
198 }
199
200 Socket Socket::connectSocket(const std::string& path)
201 {
202     // Isn't the path too long?
203     if (path.size() >= sizeof(sockaddr_un::sun_path)) {
204         const std::string msg = "Socket's path too long";
205         LOGE(msg);
206         throw IPCException(msg);
207     }
208
209     int fd = socket(AF_UNIX, SOCK_STREAM, 0);
210     if (fd == -1) {
211         const std::string msg = "Error in socket: " + getSystemErrorMessage();
212         LOGE(msg);
213         throw IPCSocketException(errno, msg);
214     }
215     setFdOptions(fd);
216
217     sockaddr_un serverAddress;
218     serverAddress.sun_family = AF_UNIX;
219     strncpy(serverAddress.sun_path, path.c_str(), sizeof(sockaddr_un::sun_path));
220
221     if (-1 == connect(fd,
222                       reinterpret_cast<struct sockaddr*>(&serverAddress),
223                       sizeof(struct sockaddr_un))) {
224         utils::close(fd);
225         const std::string msg = "Error in connect: " + getSystemErrorMessage();
226         LOGE(msg);
227         throw IPCException(msg);
228     }
229
230     // Nonblock socket
231     int flags = fcntl(fd, F_GETFL, 0);
232     if (-1 == fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
233         utils::close(fd);
234         const std::string msg = "Error in fcntl: " + getSystemErrorMessage();
235         LOGE(msg);
236         throw IPCException(msg);
237     }
238
239     return Socket(fd);
240 }
241
242 } // namespace ipc