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

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

network/os2: QLocalServer/QLocalSocket: Make sure the socket path name always starts with "\socket\" (fixes #166) [based on patch by rudi].

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