1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui 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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qunixsocketserver_p.h"
43
44 // #define QUNIXSOCKETSERVER_DEBUG
45
46 #ifdef QUNIXSOCKETSERVER_DEBUG
47 #include <QDebug>
48 #endif
49
50 #include <QtCore/qsocketnotifier.h>
51
52 extern "C" {
53 #include <sys/types.h>
54 #include <sys/socket.h>
55 #include <sys/un.h>
56 #include <unistd.h>
57 #include <errno.h>
58 };
59
60 #define UNIX_PATH_MAX 108 // From unix(7)
61
62 QT_BEGIN_NAMESPACE
63
64 class QUnixSocketServerPrivate : public QObject
65 {
66 Q_OBJECT
67 public:
QUnixSocketServerPrivate(QUnixSocketServer * parent)68 QUnixSocketServerPrivate(QUnixSocketServer * parent)
69 : QObject(), me(parent), fd(-1), maxConns(30),
70 error(QUnixSocketServer::NoError), acceptNotifier(0)
71 {}
72
73 QUnixSocketServer * me;
74 int fd;
75 int maxConns;
76 QByteArray address;
77 QUnixSocketServer::ServerError error;
78 QSocketNotifier * acceptNotifier;
79 public slots:
80 void acceptActivated();
81 };
82
83 /*!
84 \class QUnixSocketServer
85 \internal
86
87 \brief The QUnixSocketServer class provides a Unix domain socket based server.
88 \omit
89 \ingroup Platform::DeviceSpecific
90 \ingroup Platform::OS
91 \ingroup Platform::Communications
92 \endomit
93 \ingroup qws
94
95 This class makes it possible to accept incoming Unix domain socket
96 connections. Call \l QUnixSocketServer::listen() to have the server listen
97 for incoming connections on a specified path. The pure virtual
98 \l QUnixSocketServer::incomingConnection() is called each time a new
99 connection is established. Users must inherit from QUnixSocketServer and
100 implement this method.
101
102 If an error occurs, \l QUnixSocketServer::serverError() returns the type of
103 error. Errors can only occur during server establishment - that is, during a
104 call to \l QUnixSocketServer::listen(). Calling \l QUnixSocketServer::close()
105 causes QUnixSocketServer to stop listening for connections and reset its
106 state.
107
108 QUnixSocketServer is often used in conjunction with the \l QUnixSocket class.
109
110 \sa QUnixSocket
111 */
112
113 /*!
114 \enum QUnixSocketServer::ServerError
115
116 The ServerError enumeration represents the errors that can occur during server
117 establishment. The most recent error can be retrieved through a call to
118 \l QUnixSocketServer::serverError().
119
120 \value NoError No error has occurred.
121 \value InvalidPath An invalid path endpoint was passed to
122 \l QUnixSocketServer::listen(). As defined by unix(7), invalid paths
123 include an empty path, or what more than 107 characters long.
124 \value ResourceError An error acquiring or manipulating the system's socket
125 resources occurred. For example, if the process runs out of available
126 socket descriptors, a ResourceError will occur.
127 \value BindError The server was unable to bind to the specified path.
128 \value ListenError The server was unable to listen on the specified path for
129 incoming connections.
130 */
131
132 /*!
133 Create a new Unix socket server with the given \a parent.
134 */
QUnixSocketServer(QObject * parent)135 QUnixSocketServer::QUnixSocketServer(QObject *parent)
136 : QObject(parent), d(0)
137 {
138 }
139
140 /*!
141 Stops listening for incoming connection and destroys the Unix socket server.
142 */
~QUnixSocketServer()143 QUnixSocketServer::~QUnixSocketServer()
144 {
145 close();
146 if(d)
147 delete d;
148 }
149
150 /*!
151 Stop listening for incoming connections and resets the Unix socket server's
152 state. Calling this method while \l {QUnixSocketServer::isListening()}{not listening } for incoming connections is a no-op.
153
154 \sa QUnixSocketServer::listen()
155 */
close()156 void QUnixSocketServer::close()
157 {
158 if(!d)
159 return;
160
161 if(d->acceptNotifier) {
162 d->acceptNotifier->setEnabled(false);
163 delete d->acceptNotifier;
164 }
165 d->acceptNotifier = 0;
166
167 if(-1 != d->fd) {
168 #ifdef QUNIXSOCKET_DEBUG
169 int closerv =
170 #endif
171 ::close(d->fd);
172 #ifdef QUNIXSOCKET_DEBUG
173 if(0 != closerv) {
174 qDebug() << "QUnixSocketServer: Unable to close socket ("
175 << strerror(errno) << ')';
176 }
177 #endif
178 }
179 d->fd = -1;
180 d->address = QByteArray();
181 d->error = NoError;
182 }
183
184 /*!
185 Returns the last server error. Errors may only occur within a call to
186 \l QUnixSocketServer::listen(), and only when such a call fails.
187
188 This method is not destructive, so multiple calls to
189 QUnixSocketServer::serverError() will return the same value. The error is
190 only reset by an explicit call to \l QUnixSocketServer::close() or
191 by further calls to \l QUnixSocketServer::listen().
192 */
serverError() const193 QUnixSocketServer::ServerError QUnixSocketServer::serverError() const
194 {
195 if(!d)
196 return NoError;
197
198 return d->error;
199 }
200
201 /*!
202 Returns true if this server is listening for incoming connections, false
203 otherwise.
204
205 \sa QUnixSocketServer::listen()
206 */
isListening() const207 bool QUnixSocketServer::isListening() const
208 {
209 if(!d)
210 return false;
211
212 return (-1 != d->fd);
213 }
214
215 /*!
216 Tells the server to listen for incoming connections on \a path. Returns true
217 if it successfully initializes, false otherwise. In the case of failure, the
218 \l QUnixSocketServer::serverError() error status is set accordingly.
219
220 Calling this method while the server is already running will result in the
221 server begin reset, and then attempting to listen on \a path. This will not
222 affect connections established prior to the server being reset, but further
223 incoming connections on the previous path will be refused.
224
225 The server can be explicitly reset by a call to \l QUnixSocketServer::close().
226
227 \sa QUnixSocketServer::close()
228 */
listen(const QByteArray & path)229 bool QUnixSocketServer::listen(const QByteArray & path)
230 {
231 if(d) {
232 close(); // Any existing server is destroyed
233 } else {
234 d = new QUnixSocketServerPrivate(this);
235 }
236
237 if(path.isEmpty() || path.size() > UNIX_PATH_MAX) {
238 d->error = InvalidPath;
239 return false;
240 }
241 unlink( path ); // ok if this fails
242
243 // Create the socket
244 d->fd = ::socket(PF_UNIX, SOCK_STREAM, 0);
245 if(-1 == d->fd) {
246 #ifdef QUNIXSOCKETSERVER_DEBUG
247 qDebug() << "QUnixSocketServer: Unable to create socket ("
248 << strerror(errno) << ')';
249 #endif
250 close();
251 d->error = ResourceError;
252 return false;
253 }
254
255 // Construct our unix address
256 struct ::sockaddr_un addr;
257 addr.sun_family = AF_UNIX;
258 ::memcpy(addr.sun_path, path.data(), path.size());
259 if(path.size() < UNIX_PATH_MAX)
260 addr.sun_path[path.size()] = '\0';
261
262 // Attempt to bind
263 if(-1 == ::bind(d->fd, (sockaddr *)&addr, sizeof(sockaddr_un))) {
264 #ifdef QUNIXSOCKETSERVER_DEBUG
265 qDebug() << "QUnixSocketServer: Unable to bind socket ("
266 << strerror(errno) << ')';
267 #endif
268 close();
269 d->error = BindError;
270 return false;
271 }
272
273 // Listen to socket
274 if(-1 == ::listen(d->fd, d->maxConns)) {
275 #ifdef QUNIXSOCKETSERVER_DEBUG
276 qDebug() << "QUnixSocketServer: Unable to listen socket ("
277 << strerror(errno) << ')';
278 #endif
279 close();
280 d->error = ListenError;
281 return false;
282 }
283
284 // Success!
285 d->address = path;
286 d->acceptNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, d);
287 d->acceptNotifier->setEnabled(true);
288 QObject::connect(d->acceptNotifier, SIGNAL(activated(int)),
289 d, SLOT(acceptActivated()));
290
291 return true;
292 }
293
294 /*!
295 Returns the Unix path on which this server is listening. If this server is
296 not listening, and empty address will be returned.
297 */
serverAddress() const298 QByteArray QUnixSocketServer::serverAddress() const
299 {
300 if(!d)
301 return QByteArray();
302 return d->address;
303 }
304
socketDescriptor() const305 int QUnixSocketServer::socketDescriptor() const
306 {
307 if (!d)
308 return -1;
309 return d->fd;
310 }
311
312
313 /*!
314 Returns the maximum length the queue of pending connections may grow to. That
315 is, the maximum number of clients attempting to connect for which the Unix
316 socket server has not yet accepted and passed to
317 \l QUnixSocketServer::incomingConnection(). If a connection request arrives
318 with the queue full, the client may receive a connection refused notification.
319
320 By default a queue length of 30 is used.
321
322 \sa QUnixSocketServer::setMaxPendingConnections()
323 */
maxPendingConnections() const324 int QUnixSocketServer::maxPendingConnections() const
325 {
326 if(!d)
327 return 30;
328
329 return d->maxConns;
330 }
331
332 /*!
333 Sets the maximum length the queue of pending connections may grow to
334 \a numConnections. This value will only apply to
335 \l QUnixSocketServer::listen() calls made following the value change - it will
336 not be retroactively applied.
337
338 \sa QUnixSocketServer::maxPendingConnections()
339 */
setMaxPendingConnections(int numConnections)340 void QUnixSocketServer::setMaxPendingConnections(int numConnections)
341 {
342 Q_ASSERT(numConnections >= 1);
343 if(!d)
344 d = new QUnixSocketServerPrivate(this);
345
346 d->maxConns = numConnections;
347 }
348
349 /*!
350 \fn void QUnixSocketServer::incomingConnection(int socketDescriptor)
351
352 This method is invoked each time a new incoming connection is established with
353 the server. Clients must reimplement this function in their QUnixSocketServer
354 derived class to handle the connection.
355
356 A common approach to handling the connection is to pass \a socketDescriptor to
357 a QUnixSocket instance.
358
359 \sa QUnixSocket
360 */
361
acceptActivated()362 void QUnixSocketServerPrivate::acceptActivated()
363 {
364 ::sockaddr_un r;
365 socklen_t len = sizeof(sockaddr_un);
366 int connsock = ::accept(fd, (sockaddr *)&r, &len);
367 #ifdef QUNIXSOCKETSERVER_DEBUG
368 qDebug() << "QUnixSocketServer: Accept connection " << connsock;
369 #endif
370 if(-1 != connsock)
371 me->incomingConnection(connsock);
372 }
373
374 QT_END_NAMESPACE
375
376 #include "qunixsocketserver.moc"
377