|
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" |