0
|
1 |
/****************************************************************************
|
|
2 |
**
|
|
3 |
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
4 |
** All rights reserved.
|
|
5 |
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
6 |
**
|
|
7 |
** This file is part of the QtGui module of the Qt Toolkit.
|
|
8 |
**
|
|
9 |
** $QT_BEGIN_LICENSE:LGPL$
|
|
10 |
** No Commercial Usage
|
|
11 |
** This file contains pre-release code and may not be distributed.
|
|
12 |
** You may use this file in accordance with the terms and conditions
|
|
13 |
** contained in the Technology Preview License Agreement accompanying
|
|
14 |
** this package.
|
|
15 |
**
|
|
16 |
** GNU Lesser General Public License Usage
|
|
17 |
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
18 |
** General Public License version 2.1 as published by the Free Software
|
|
19 |
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
20 |
** packaging of this file. Please review the following information to
|
|
21 |
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
22 |
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
23 |
**
|
|
24 |
** In addition, as a special exception, Nokia gives you certain additional
|
|
25 |
** rights. These rights are described in the Nokia Qt LGPL Exception
|
|
26 |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
27 |
**
|
|
28 |
** If you have questions regarding the use of this file, please contact
|
|
29 |
** Nokia at qt-info@nokia.com.
|
|
30 |
**
|
|
31 |
**
|
|
32 |
**
|
|
33 |
**
|
|
34 |
**
|
|
35 |
**
|
|
36 |
**
|
|
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:
|
|
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 |
*/
|
|
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 |
*/
|
|
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 |
*/
|
|
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 |
*/
|
|
193 |
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 |
*/
|
|
207 |
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 |
*/
|
|
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 |
*/
|
|
298 |
QByteArray QUnixSocketServer::serverAddress() const
|
|
299 |
{
|
|
300 |
if(!d)
|
|
301 |
return QByteArray();
|
|
302 |
return d->address;
|
|
303 |
}
|
|
304 |
|
|
305 |
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 |
*/
|
|
324 |
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 |
*/
|
|
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 |
|
|
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"
|