source: trunk/src/network/socket/qlocalsocket_unix.cpp@ 807

Last change on this file since 807 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

File size: 18.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtNetwork module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qlocalsocket.h"
43#include "qlocalsocket_p.h"
44#include "qnet_unix_p.h"
45
46#ifndef QT_NO_LOCALSOCKET
47
48#include <sys/types.h>
49#include <sys/socket.h>
50#include <sys/un.h>
51#include <unistd.h>
52#include <fcntl.h>
53#include <errno.h>
54
55#include <qdatetime.h>
56#include <qdir.h>
57#include <qdebug.h>
58
59#ifdef Q_OS_VXWORKS
60# include <selectLib.h>
61#endif
62
63#define QT_CONNECT_TIMEOUT 30000
64
65QT_BEGIN_NAMESPACE
66
67QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
68 delayConnect(0),
69 connectTimer(0),
70 connectingSocket(-1),
71 connectingOpenMode(0),
72 state(QLocalSocket::UnconnectedState)
73{
74}
75
76void QLocalSocketPrivate::init()
77{
78 Q_Q(QLocalSocket);
79 // QIODevice signals
80 q->connect(&unixSocket, SIGNAL(aboutToClose()), q, SIGNAL(aboutToClose()));
81 q->connect(&unixSocket, SIGNAL(bytesWritten(qint64)),
82 q, SIGNAL(bytesWritten(qint64)));
83 q->connect(&unixSocket, SIGNAL(readyRead()), q, SIGNAL(readyRead()));
84 // QAbstractSocket signals
85 q->connect(&unixSocket, SIGNAL(connected()), q, SIGNAL(connected()));
86 q->connect(&unixSocket, SIGNAL(disconnected()), q, SIGNAL(disconnected()));
87 q->connect(&unixSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
88 q, SLOT(_q_stateChanged(QAbstractSocket::SocketState)));
89 q->connect(&unixSocket, SIGNAL(error(QAbstractSocket::SocketError)),
90 q, SLOT(_q_error(QAbstractSocket::SocketError)));
91 q->connect(&unixSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
92 unixSocket.setParent(q);
93}
94
95void QLocalSocketPrivate::_q_error(QAbstractSocket::SocketError socketError)
96{
97 Q_Q(QLocalSocket);
98 QString function = QLatin1String("QLocalSocket");
99 QLocalSocket::LocalSocketError error = (QLocalSocket::LocalSocketError)socketError;
100 QString errorString = generateErrorString(error, function);
101 q->setErrorString(errorString);
102 emit q->error(error);
103}
104
105void QLocalSocketPrivate::_q_stateChanged(QAbstractSocket::SocketState newState)
106{
107 Q_Q(QLocalSocket);
108 QLocalSocket::LocalSocketState currentState = state;
109 switch(newState) {
110 case QAbstractSocket::UnconnectedState:
111 state = QLocalSocket::UnconnectedState;
112 serverName.clear();
113 fullServerName.clear();
114 break;
115 case QAbstractSocket::ConnectingState:
116 state = QLocalSocket::ConnectingState;
117 break;
118 case QAbstractSocket::ConnectedState:
119 state = QLocalSocket::ConnectedState;
120 break;
121 case QAbstractSocket::ClosingState:
122 state = QLocalSocket::ClosingState;
123 break;
124 default:
125#if defined QLOCALSOCKET_DEBUG
126 qWarning() << "QLocalSocket::Unhandled socket state change:" << newState;
127#endif
128 return;
129 }
130 if (currentState != state)
131 emit q->stateChanged(state);
132}
133
134QString QLocalSocketPrivate::generateErrorString(QLocalSocket::LocalSocketError error, const QString &function) const
135{
136 QString errorString;
137 switch (error) {
138 case QLocalSocket::ConnectionRefusedError:
139 errorString = QLocalSocket::tr("%1: Connection refused").arg(function);
140 break;
141 case QLocalSocket::PeerClosedError:
142 errorString = QLocalSocket::tr("%1: Remote closed").arg(function);
143 break;
144 case QLocalSocket::ServerNotFoundError:
145 errorString = QLocalSocket::tr("%1: Invalid name").arg(function);
146 break;
147 case QLocalSocket::SocketAccessError:
148 errorString = QLocalSocket::tr("%1: Socket access error").arg(function);
149 break;
150 case QLocalSocket::SocketResourceError:
151 errorString = QLocalSocket::tr("%1: Socket resource error").arg(function);
152 break;
153 case QLocalSocket::SocketTimeoutError:
154 errorString = QLocalSocket::tr("%1: Socket operation timed out").arg(function);
155 break;
156 case QLocalSocket::DatagramTooLargeError:
157 errorString = QLocalSocket::tr("%1: Datagram too large").arg(function);
158 break;
159 case QLocalSocket::ConnectionError:
160 errorString = QLocalSocket::tr("%1: Connection error").arg(function);
161 break;
162 case QLocalSocket::UnsupportedSocketOperationError:
163 errorString = QLocalSocket::tr("%1: The socket operation is not supported").arg(function);
164 break;
165 case QLocalSocket::UnknownSocketError:
166 default:
167 errorString = QLocalSocket::tr("%1: Unknown error %2").arg(function).arg(errno);
168 }
169 return errorString;
170}
171
172void QLocalSocketPrivate::errorOccurred(QLocalSocket::LocalSocketError error, const QString &function)
173{
174 Q_Q(QLocalSocket);
175 switch (error) {
176 case QLocalSocket::ConnectionRefusedError:
177 unixSocket.setSocketError(QAbstractSocket::ConnectionRefusedError);
178 break;
179 case QLocalSocket::PeerClosedError:
180 unixSocket.setSocketError(QAbstractSocket::RemoteHostClosedError);
181 break;
182 case QLocalSocket::ServerNotFoundError:
183 unixSocket.setSocketError(QAbstractSocket::HostNotFoundError);
184 break;
185 case QLocalSocket::SocketAccessError:
186 unixSocket.setSocketError(QAbstractSocket::SocketAccessError);
187 break;
188 case QLocalSocket::SocketResourceError:
189 unixSocket.setSocketError(QAbstractSocket::SocketResourceError);
190 break;
191 case QLocalSocket::SocketTimeoutError:
192 unixSocket.setSocketError(QAbstractSocket::SocketTimeoutError);
193 break;
194 case QLocalSocket::DatagramTooLargeError:
195 unixSocket.setSocketError(QAbstractSocket::DatagramTooLargeError);
196 break;
197 case QLocalSocket::ConnectionError:
198 unixSocket.setSocketError(QAbstractSocket::NetworkError);
199 break;
200 case QLocalSocket::UnsupportedSocketOperationError:
201 unixSocket.setSocketError(QAbstractSocket::UnsupportedSocketOperationError);
202 break;
203 case QLocalSocket::UnknownSocketError:
204 default:
205 unixSocket.setSocketError(QAbstractSocket::UnknownSocketError);
206 }
207
208 QString errorString = generateErrorString(error, function);
209 q->setErrorString(errorString);
210 emit q->error(error);
211
212 // errors cause a disconnect
213 unixSocket.setSocketState(QAbstractSocket::UnconnectedState);
214 bool stateChanged = (state != QLocalSocket::UnconnectedState);
215 state = QLocalSocket::UnconnectedState;
216 q->close();
217 if (stateChanged)
218 q->emit stateChanged(state);
219}
220
221void QLocalSocket::connectToServer(const QString &name, OpenMode openMode)
222{
223 Q_D(QLocalSocket);
224 if (state() == ConnectedState
225 || state() == ConnectingState)
226 return;
227
228 d->errorString.clear();
229 d->unixSocket.setSocketState(QAbstractSocket::ConnectingState);
230 d->state = ConnectingState;
231 emit stateChanged(d->state);
232
233 if (name.isEmpty()) {
234 d->errorOccurred(ServerNotFoundError,
235 QLatin1String("QLocalSocket::connectToServer"));
236 return;
237 }
238
239 // create the socket
240 if (-1 == (d->connectingSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, 0))) {
241 d->errorOccurred(UnsupportedSocketOperationError,
242 QLatin1String("QLocalSocket::connectToServer"));
243 return;
244 }
245#ifndef Q_OS_SYMBIAN
246 // set non blocking so we can try to connect and it wont wait
247 int flags = fcntl(d->connectingSocket, F_GETFL, 0);
248 if (-1 == flags
249 || -1 == (fcntl(d->connectingSocket, F_SETFL, flags | O_NONBLOCK))) {
250 d->errorOccurred(UnknownSocketError,
251 QLatin1String("QLocalSocket::connectToServer"));
252 return;
253 }
254#endif
255
256 // _q_connectToSocket does the actual connecting
257 d->connectingName = name;
258 d->connectingOpenMode = openMode;
259 d->_q_connectToSocket();
260}
261
262/*!
263 \internal
264
265 Tries to connect connectingName and connectingOpenMode
266
267 \sa connectToServer() waitForConnected()
268 */
269void QLocalSocketPrivate::_q_connectToSocket()
270{
271 Q_Q(QLocalSocket);
272 QString connectingPathName;
273
274 // determine the full server path
275 if (connectingName.startsWith(QLatin1Char('/'))) {
276 connectingPathName = connectingName;
277 } else {
278 connectingPathName = QDir::tempPath();
279 connectingPathName += QLatin1Char('/') + connectingName;
280 }
281
282 struct sockaddr_un name;
283 name.sun_family = PF_UNIX;
284 if (sizeof(name.sun_path) < (uint)connectingPathName.toLatin1().size() + 1) {
285 QString function = QLatin1String("QLocalSocket::connectToServer");
286 errorOccurred(QLocalSocket::ServerNotFoundError, function);
287 return;
288 }
289 ::memcpy(name.sun_path, connectingPathName.toLatin1().data(),
290 connectingPathName.toLatin1().size() + 1);
291 if (-1 == qt_safe_connect(connectingSocket, (struct sockaddr *)&name, sizeof(name))) {
292 QString function = QLatin1String("QLocalSocket::connectToServer");
293 switch (errno)
294 {
295 case EINVAL:
296 case ECONNREFUSED:
297 errorOccurred(QLocalSocket::ConnectionRefusedError, function);
298 break;
299 case ENOENT:
300 errorOccurred(QLocalSocket::ServerNotFoundError, function);
301 break;
302 case EACCES:
303 case EPERM:
304 errorOccurred(QLocalSocket::SocketAccessError, function);
305 break;
306 case ETIMEDOUT:
307 errorOccurred(QLocalSocket::SocketTimeoutError, function);
308 break;
309 case EAGAIN:
310 // Try again later, all of the sockets listening are full
311 if (!delayConnect) {
312 delayConnect = new QSocketNotifier(connectingSocket, QSocketNotifier::Write, q);
313 q->connect(delayConnect, SIGNAL(activated(int)), q, SLOT(_q_connectToSocket()));
314 }
315 if (!connectTimer) {
316 connectTimer = new QTimer(q);
317 q->connect(connectTimer, SIGNAL(timeout()),
318 q, SLOT(_q_abortConnectionAttempt()),
319 Qt::DirectConnection);
320 connectTimer->start(QT_CONNECT_TIMEOUT);
321 }
322 delayConnect->setEnabled(true);
323 break;
324 default:
325 errorOccurred(QLocalSocket::UnknownSocketError, function);
326 }
327 return;
328 }
329
330 // connected!
331 cancelDelayedConnect();
332
333 serverName = connectingName;
334 fullServerName = connectingPathName;
335 if (unixSocket.setSocketDescriptor(connectingSocket,
336 QAbstractSocket::ConnectedState, connectingOpenMode)) {
337 q->QIODevice::open(connectingOpenMode);
338 q->emit connected();
339 } else {
340 QString function = QLatin1String("QLocalSocket::connectToServer");
341 errorOccurred(QLocalSocket::UnknownSocketError, function);
342 }
343 connectingSocket = -1;
344 connectingName.clear();
345 connectingOpenMode = 0;
346}
347
348bool QLocalSocket::setSocketDescriptor(quintptr socketDescriptor,
349 LocalSocketState socketState, OpenMode openMode)
350{
351 Q_D(QLocalSocket);
352 QAbstractSocket::SocketState newSocketState = QAbstractSocket::UnconnectedState;
353 switch (socketState) {
354 case ConnectingState:
355 newSocketState = QAbstractSocket::ConnectingState;
356 break;
357 case ConnectedState:
358 newSocketState = QAbstractSocket::ConnectedState;
359 break;
360 case ClosingState:
361 newSocketState = QAbstractSocket::ClosingState;
362 break;
363 case UnconnectedState:
364 newSocketState = QAbstractSocket::UnconnectedState;
365 break;
366 }
367 QIODevice::open(openMode);
368 d->state = socketState;
369 return d->unixSocket.setSocketDescriptor(socketDescriptor,
370 newSocketState, openMode);
371}
372
373void QLocalSocketPrivate::_q_abortConnectionAttempt()
374{
375 Q_Q(QLocalSocket);
376 q->close();
377}
378
379void QLocalSocketPrivate::cancelDelayedConnect()
380{
381 if (delayConnect) {
382 delayConnect->setEnabled(false);
383 delete delayConnect;
384 delayConnect = 0;
385 connectTimer->stop();
386 delete connectTimer;
387 connectTimer = 0;
388 }
389}
390
391quintptr QLocalSocket::socketDescriptor() const
392{
393 Q_D(const QLocalSocket);
394 return d->unixSocket.socketDescriptor();
395}
396
397qint64 QLocalSocket::readData(char *data, qint64 c)
398{
399 Q_D(QLocalSocket);
400 return d->unixSocket.readData(data, c);
401}
402
403qint64 QLocalSocket::writeData(const char *data, qint64 c)
404{
405 Q_D(QLocalSocket);
406 return d->unixSocket.writeData(data, c);
407}
408
409void QLocalSocket::abort()
410{
411 Q_D(QLocalSocket);
412 d->unixSocket.abort();
413}
414
415qint64 QLocalSocket::bytesAvailable() const
416{
417 Q_D(const QLocalSocket);
418 return QIODevice::bytesAvailable() + d->unixSocket.bytesAvailable();
419}
420
421qint64 QLocalSocket::bytesToWrite() const
422{
423 Q_D(const QLocalSocket);
424 return d->unixSocket.bytesToWrite();
425}
426
427bool QLocalSocket::canReadLine() const
428{
429 Q_D(const QLocalSocket);
430 return QIODevice::canReadLine() || d->unixSocket.canReadLine();
431}
432
433void QLocalSocket::close()
434{
435 Q_D(QLocalSocket);
436 d->unixSocket.close();
437 d->cancelDelayedConnect();
438 if (d->connectingSocket != -1)
439 ::close(d->connectingSocket);
440 d->connectingSocket = -1;
441 d->connectingName.clear();
442 d->connectingOpenMode = 0;
443 d->serverName.clear();
444 d->fullServerName.clear();
445 QIODevice::close();
446}
447
448bool QLocalSocket::waitForBytesWritten(int msecs)
449{
450 Q_D(QLocalSocket);
451 return d->unixSocket.waitForBytesWritten(msecs);
452}
453
454bool QLocalSocket::flush()
455{
456 Q_D(QLocalSocket);
457 return d->unixSocket.flush();
458}
459
460void QLocalSocket::disconnectFromServer()
461{
462 Q_D(QLocalSocket);
463 d->unixSocket.disconnectFromHost();
464}
465
466QLocalSocket::LocalSocketError QLocalSocket::error() const
467{
468 Q_D(const QLocalSocket);
469 switch (d->unixSocket.error()) {
470 case QAbstractSocket::ConnectionRefusedError:
471 return QLocalSocket::ConnectionRefusedError;
472 case QAbstractSocket::RemoteHostClosedError:
473 return QLocalSocket::PeerClosedError;
474 case QAbstractSocket::HostNotFoundError:
475 return QLocalSocket::ServerNotFoundError;
476 case QAbstractSocket::SocketAccessError:
477 return QLocalSocket::SocketAccessError;
478 case QAbstractSocket::SocketResourceError:
479 return QLocalSocket::SocketResourceError;
480 case QAbstractSocket::SocketTimeoutError:
481 return QLocalSocket::SocketTimeoutError;
482 case QAbstractSocket::DatagramTooLargeError:
483 return QLocalSocket::DatagramTooLargeError;
484 case QAbstractSocket::NetworkError:
485 return QLocalSocket::ConnectionError;
486 case QAbstractSocket::UnsupportedSocketOperationError:
487 return QLocalSocket::UnsupportedSocketOperationError;
488 case QAbstractSocket::UnknownSocketError:
489 return QLocalSocket::UnknownSocketError;
490 default:
491#if defined QLOCALSOCKET_DEBUG
492 qWarning() << "QLocalSocket error not handled:" << d->unixSocket.error();
493#endif
494 break;
495 }
496 return UnknownSocketError;
497}
498
499bool QLocalSocket::isValid() const
500{
501 Q_D(const QLocalSocket);
502 return d->unixSocket.isValid();
503}
504
505qint64 QLocalSocket::readBufferSize() const
506{
507 Q_D(const QLocalSocket);
508 return d->unixSocket.readBufferSize();
509}
510
511void QLocalSocket::setReadBufferSize(qint64 size)
512{
513 Q_D(QLocalSocket);
514 d->unixSocket.setReadBufferSize(size);
515}
516
517bool QLocalSocket::waitForConnected(int msec)
518{
519 Q_D(QLocalSocket);
520 if (state() != ConnectingState)
521 return (state() == ConnectedState);
522
523 fd_set fds;
524 FD_ZERO(&fds);
525 FD_SET(d->connectingSocket, &fds);
526
527 timeval timeout;
528 timeout.tv_sec = msec / 1000;
529 timeout.tv_usec = (msec % 1000) * 1000;
530
531 // timeout can not be 0 or else select will return an error.
532 if (0 == msec)
533 timeout.tv_usec = 1000;
534
535 int result = -1;
536 // on Linux timeout will be updated by select, but _not_ on other systems.
537 QTime timer;
538 timer.start();
539 while (state() == ConnectingState
540 && (-1 == msec || timer.elapsed() < msec)) {
541#ifdef Q_OS_SYMBIAN
542 // On Symbian, ready-to-write is signaled when non-blocking socket
543 // connect is finised. Is ready-to-read really used on other
544 // UNIX paltforms when using non-blocking AF_UNIX socket?
545 result = ::select(d->connectingSocket + 1, 0, &fds, 0, &timeout);
546#else
547 result = ::select(d->connectingSocket + 1, &fds, 0, 0, &timeout);
548#endif
549 if (-1 == result && errno != EINTR) {
550 d->errorOccurred( QLocalSocket::UnknownSocketError,
551 QLatin1String("QLocalSocket::waitForConnected"));
552 break;
553 }
554 if (result > 0)
555 d->_q_connectToSocket();
556 }
557
558 return (state() == ConnectedState);
559}
560
561bool QLocalSocket::waitForDisconnected(int msecs)
562{
563 Q_D(QLocalSocket);
564 if (state() == UnconnectedState) {
565 qWarning() << "QLocalSocket::waitForDisconnected() is not allowed in UnconnectedState";
566 return false;
567 }
568 return (d->unixSocket.waitForDisconnected(msecs));
569}
570
571bool QLocalSocket::waitForReadyRead(int msecs)
572{
573 Q_D(QLocalSocket);
574 if (state() == QLocalSocket::UnconnectedState)
575 return false;
576 return (d->unixSocket.waitForReadyRead(msecs));
577}
578
579QT_END_NAMESPACE
580
581#endif
Note: See TracBrowser for help on using the repository browser.