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

Last change on this file since 352 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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