1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qlocalserver.h"
41#include "qlocalserver_p.h"
42#include "qlocalsocket.h"
43#include "qlocalsocket_p.h"
44#include "qnet_unix_p.h"
45#include "qtemporarydir.h"
46
47#include <sys/socket.h>
48#include <sys/un.h>
49
50#include <qdebug.h>
51#include <qdir.h>
52#include <qdatetime.h>
53
54#ifdef Q_OS_VXWORKS
55# include <selectLib.h>
56#endif
57
58QT_BEGIN_NAMESPACE
59
60void QLocalServerPrivate::init()
61{
62}
63
64bool QLocalServerPrivate::removeServer(const QString &name)
65{
66 QString fileName;
67 if (name.startsWith(QLatin1Char('/'))) {
68 fileName = name;
69 } else {
70 fileName = QDir::cleanPath(QDir::tempPath());
71 fileName += QLatin1Char('/') + name;
72 }
73 if (QFile::exists(fileName))
74 return QFile::remove(fileName);
75 else
76 return true;
77}
78
79bool QLocalServerPrivate::listen(const QString &requestedServerName)
80{
81 Q_Q(QLocalServer);
82
83 // determine the full server path
84 if (requestedServerName.startsWith(QLatin1Char('/'))) {
85 fullServerName = requestedServerName;
86 } else {
87 fullServerName = QDir::cleanPath(QDir::tempPath());
88 fullServerName += QLatin1Char('/') + requestedServerName;
89 }
90 serverName = requestedServerName;
91
92 QByteArray encodedTempPath;
93 const QByteArray encodedFullServerName = QFile::encodeName(fullServerName);
94 QScopedPointer<QTemporaryDir> tempDir;
95
96 // Check any of the flags
97 if (socketOptions & QLocalServer::WorldAccessOption) {
98 QFileInfo serverNameFileInfo(fullServerName);
99 tempDir.reset(new QTemporaryDir(serverNameFileInfo.absolutePath() + QLatin1Char('/')));
100 if (!tempDir->isValid()) {
101 setError(QLatin1String("QLocalServer::listen"));
102 return false;
103 }
104 encodedTempPath = QFile::encodeName(tempDir->path() + QLatin1String("/s"));
105 }
106
107 // create the unix socket
108 listenSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, 0);
109 if (-1 == listenSocket) {
110 setError(QLatin1String("QLocalServer::listen"));
111 closeServer();
112 return false;
113 }
114
115 // Construct the unix address
116 struct ::sockaddr_un addr;
117 addr.sun_family = PF_UNIX;
118 if (sizeof(addr.sun_path) < (uint)encodedFullServerName.size() + 1) {
119 setError(QLatin1String("QLocalServer::listen"));
120 closeServer();
121 return false;
122 }
123
124 if (socketOptions & QLocalServer::WorldAccessOption) {
125 if (sizeof(addr.sun_path) < (uint)encodedTempPath.size() + 1) {
126 setError(QLatin1String("QLocalServer::listen"));
127 closeServer();
128 return false;
129 }
130 ::memcpy(addr.sun_path, encodedTempPath.constData(),
131 encodedTempPath.size() + 1);
132 } else {
133 ::memcpy(addr.sun_path, encodedFullServerName.constData(),
134 encodedFullServerName.size() + 1);
135 }
136
137 // bind
138 if(-1 == QT_SOCKET_BIND(listenSocket, (sockaddr *)&addr, sizeof(sockaddr_un))) {
139 setError(QLatin1String("QLocalServer::listen"));
140 // if address is in use already, just close the socket, but do not delete the file
141 if(errno == EADDRINUSE)
142 QT_CLOSE(listenSocket);
143 // otherwise, close the socket and delete the file
144 else
145 closeServer();
146 listenSocket = -1;
147 return false;
148 }
149
150 // listen for connections
151 if (-1 == qt_safe_listen(listenSocket, 50)) {
152 setError(QLatin1String("QLocalServer::listen"));
153 closeServer();
154 listenSocket = -1;
155 if (error != QAbstractSocket::AddressInUseError)
156 QFile::remove(fullServerName);
157 return false;
158 }
159
160 if (socketOptions & QLocalServer::WorldAccessOption) {
161 mode_t mode = 000;
162
163 if (socketOptions & QLocalServer::UserAccessOption)
164 mode |= S_IRWXU;
165
166 if (socketOptions & QLocalServer::GroupAccessOption)
167 mode |= S_IRWXG;
168
169 if (socketOptions & QLocalServer::OtherAccessOption)
170 mode |= S_IRWXO;
171
172 if (::chmod(encodedTempPath.constData(), mode) == -1) {
173 setError(QLatin1String("QLocalServer::listen"));
174 closeServer();
175 return false;
176 }
177
178 if (::rename(encodedTempPath.constData(), encodedFullServerName.constData()) == -1) {
179 setError(QLatin1String("QLocalServer::listen"));
180 closeServer();
181 return false;
182 }
183 }
184
185 Q_ASSERT(!socketNotifier);
186 socketNotifier = new QSocketNotifier(listenSocket,
187 QSocketNotifier::Read, q);
188 q->connect(socketNotifier, SIGNAL(activated(QSocketDescriptor)),
189 q, SLOT(_q_onNewConnection()));
190 socketNotifier->setEnabled(maxPendingConnections > 0);
191 return true;
192}
193
194bool QLocalServerPrivate::listen(qintptr socketDescriptor)
195{
196 Q_Q(QLocalServer);
197
198 // Attach to the localsocket
199 listenSocket = socketDescriptor;
200
201 ::fcntl(listenSocket, F_SETFD, FD_CLOEXEC);
202 ::fcntl(listenSocket, F_SETFL, ::fcntl(listenSocket, F_GETFL) | O_NONBLOCK);
203
204#ifdef Q_OS_LINUX
205 struct ::sockaddr_un addr;
206 QT_SOCKLEN_T len = sizeof(addr);
207 memset(&addr, 0, sizeof(addr));
208 if (0 == ::getsockname(listenSocket, (sockaddr *)&addr, &len)) {
209 // check for absract sockets
210 if (addr.sun_family == PF_UNIX && addr.sun_path[0] == 0) {
211 addr.sun_path[0] = '@';
212 }
213 QString name = QString::fromLatin1(addr.sun_path);
214 if (!name.isEmpty()) {
215 fullServerName = name;
216 serverName = fullServerName.mid(fullServerName.lastIndexOf(QLatin1Char('/')) + 1);
217 if (serverName.isEmpty()) {
218 serverName = fullServerName;
219 }
220 }
221 }
222#else
223 serverName.clear();
224 fullServerName.clear();
225#endif
226
227 Q_ASSERT(!socketNotifier);
228 socketNotifier = new QSocketNotifier(listenSocket,
229 QSocketNotifier::Read, q);
230 q->connect(socketNotifier, SIGNAL(activated(QSocketDescriptor)),
231 q, SLOT(_q_onNewConnection()));
232 socketNotifier->setEnabled(maxPendingConnections > 0);
233 return true;
234}
235
236/*!
237 \internal
238
239 \sa QLocalServer::closeServer()
240 */
241void QLocalServerPrivate::closeServer()
242{
243 if (socketNotifier) {
244 socketNotifier->setEnabled(false); // Otherwise, closed socket is checked before deleter runs
245 socketNotifier->deleteLater();
246 socketNotifier = nullptr;
247 }
248
249 if (-1 != listenSocket)
250 QT_CLOSE(listenSocket);
251 listenSocket = -1;
252
253 if (!fullServerName.isEmpty())
254 QFile::remove(fullServerName);
255}
256
257/*!
258 \internal
259
260 We have received a notification that we can read on the listen socket.
261 Accept the new socket.
262 */
263void QLocalServerPrivate::_q_onNewConnection()
264{
265 Q_Q(QLocalServer);
266 if (-1 == listenSocket)
267 return;
268
269 ::sockaddr_un addr;
270 QT_SOCKLEN_T length = sizeof(sockaddr_un);
271 int connectedSocket = qt_safe_accept(listenSocket, (sockaddr *)&addr, &length);
272 if(-1 == connectedSocket) {
273 setError(QLatin1String("QLocalSocket::activated"));
274 closeServer();
275 } else {
276 socketNotifier->setEnabled(pendingConnections.size()
277 <= maxPendingConnections);
278 q->incomingConnection(connectedSocket);
279 }
280}
281
282void QLocalServerPrivate::waitForNewConnection(int msec, bool *timedOut)
283{
284 pollfd pfd = qt_make_pollfd(listenSocket, POLLIN);
285
286 switch (qt_poll_msecs(&pfd, 1, msec)) {
287 case 0:
288 if (timedOut)
289 *timedOut = true;
290
291 return;
292 break;
293 default:
294 if ((pfd.revents & POLLNVAL) == 0) {
295 _q_onNewConnection();
296 return;
297 }
298
299 errno = EBADF;
300 Q_FALLTHROUGH();
301 case -1:
302 setError(QLatin1String("QLocalServer::waitForNewConnection"));
303 closeServer();
304 break;
305 }
306}
307
308void QLocalServerPrivate::setError(const QString &function)
309{
310 if (EAGAIN == errno)
311 return;
312
313 switch (errno) {
314 case EACCES:
315 errorString = QLocalServer::tr("%1: Permission denied").arg(function);
316 error = QAbstractSocket::SocketAccessError;
317 break;
318 case ELOOP:
319 case ENOENT:
320 case ENAMETOOLONG:
321 case EROFS:
322 case ENOTDIR:
323 errorString = QLocalServer::tr("%1: Name error").arg(function);
324 error = QAbstractSocket::HostNotFoundError;
325 break;
326 case EADDRINUSE:
327 errorString = QLocalServer::tr("%1: Address in use").arg(function);
328 error = QAbstractSocket::AddressInUseError;
329 break;
330
331 default:
332 errorString = QLocalServer::tr("%1: Unknown error %2")
333 .arg(function).arg(errno);
334 error = QAbstractSocket::UnknownSocketError;
335#if defined QLOCALSERVER_DEBUG
336 qWarning() << errorString << "fullServerName:" << fullServerName;
337#endif
338 }
339}
340
341QT_END_NAMESPACE
342