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

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

network: Implemented QLocalServer/QLocalSocket/QNativeSocketEngine OS/2 bits (basically a copy of the Unix port with IPv6 stuff removed).

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** Copyright (C) 2009 netlabs.org. OS/2 parts.
7**
8** This file is part of the QtNetwork module of the Qt Toolkit.
9**
10** $QT_BEGIN_LICENSE:LGPL$
11** Commercial Usage
12** Licensees holding valid Qt Commercial licenses may use this file in
13** accordance with the Qt Commercial License Agreement provided with the
14** Software or, alternatively, in accordance with the terms contained in
15** a written agreement between you and Nokia.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Nokia gives you certain
26** additional rights. These rights are described in the Nokia Qt LGPL
27** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
28** 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 are unsure which license is appropriate for your use, please
39** contact the sales department 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 }
166 return errorString;
167}
168
169void QLocalSocketPrivate::errorOccurred(QLocalSocket::LocalSocketError error, const QString &function)
170{
171 Q_Q(QLocalSocket);
172 switch (error) {
173 case QLocalSocket::ConnectionRefusedError:
174 unixSocket.setSocketError(QAbstractSocket::ConnectionRefusedError);
175 break;
176 case QLocalSocket::PeerClosedError:
177 unixSocket.setSocketError(QAbstractSocket::RemoteHostClosedError);
178 break;
179 case QLocalSocket::ServerNotFoundError:
180 unixSocket.setSocketError(QAbstractSocket::HostNotFoundError);
181 break;
182 case QLocalSocket::SocketAccessError:
183 unixSocket.setSocketError(QAbstractSocket::SocketAccessError);
184 break;
185 case QLocalSocket::SocketResourceError:
186 unixSocket.setSocketError(QAbstractSocket::SocketResourceError);
187 break;
188 case QLocalSocket::SocketTimeoutError:
189 unixSocket.setSocketError(QAbstractSocket::SocketTimeoutError);
190 break;
191 case QLocalSocket::DatagramTooLargeError:
192 unixSocket.setSocketError(QAbstractSocket::DatagramTooLargeError);
193 break;
194 case QLocalSocket::ConnectionError:
195 unixSocket.setSocketError(QAbstractSocket::NetworkError);
196 break;
197 case QLocalSocket::UnsupportedSocketOperationError:
198 unixSocket.setSocketError(QAbstractSocket::UnsupportedSocketOperationError);
199 break;
200 case QLocalSocket::UnknownSocketError:
201 default:
202 unixSocket.setSocketError(QAbstractSocket::UnknownSocketError);
203 }
204
205 QString errorString = generateErrorString(error, function);
206 q->setErrorString(errorString);
207 emit q->error(error);
208
209 // errors cause a disconnect
210 unixSocket.setSocketState(QAbstractSocket::UnconnectedState);
211 bool stateChanged = (state != QLocalSocket::UnconnectedState);
212 state = QLocalSocket::UnconnectedState;
213 q->close();
214 if (stateChanged)
215 q->emit stateChanged(state);
216}
217
218void QLocalSocket::connectToServer(const QString &name, OpenMode openMode)
219{
220 Q_D(QLocalSocket);
221 if (state() == ConnectedState
222 || state() == ConnectingState)
223 return;
224
225 d->errorString = QString();
226 d->unixSocket.setSocketState(QAbstractSocket::ConnectingState);
227 d->state = ConnectingState;
228 emit stateChanged(d->state);
229
230 if (name.isEmpty()) {
231 d->errorOccurred(ServerNotFoundError,
232 QLatin1String("QLocalSocket::connectToServer"));
233 return;
234 }
235
236 // create the socket
237 if (-1 == (d->connectingSocket = qSocket(PF_UNIX, SOCK_STREAM, 0))) {
238 d->errorOccurred(UnsupportedSocketOperationError,
239 QLatin1String("QLocalSocket::connectToServer"));
240 return;
241 }
242
243 // set non blocking so we can try to connect and it wont wait
244 int flags = fcntl(d->connectingSocket, F_GETFL, 0);
245 if (-1 == flags
246 || -1 == (fcntl(d->connectingSocket, F_SETFL, flags | O_NONBLOCK))) {
247 d->errorOccurred(UnknownSocketError,
248 QLatin1String("QLocalSocket::connectToServer"));
249 return;
250 }
251
252 // _q_connectToSocket does the actual connecting
253 d->connectingName = name;
254 d->connectingOpenMode = openMode;
255 d->_q_connectToSocket();
256}
257
258/*!
259 \internal
260
261 Tries to connect connectingName and connectingOpenMode
262
263 \sa connectToServer() waitForConnected()
264 */
265void QLocalSocketPrivate::_q_connectToSocket()
266{
267 Q_Q(QLocalSocket);
268 QString connectingPathName;
269
270 // determine the full server path
271 if (connectingName.startsWith(QLatin1Char('/'))) {
272 connectingPathName = connectingName;
273 } else {
274 connectingPathName = QDir::tempPath();
275 connectingPathName += QLatin1Char('/') + connectingName;
276 }
277
278 struct sockaddr_un name;
279 name.sun_family = PF_UNIX;
280 if (sizeof(name.sun_path) < (uint)connectingPathName.toLatin1().size() + 1) {
281 QString function = QLatin1String("QLocalSocket::connectToServer");
282 errorOccurred(QLocalSocket::ServerNotFoundError, function);
283 return;
284 }
285 ::memcpy(name.sun_path, connectingPathName.toLatin1().data(),
286 connectingPathName.toLatin1().size() + 1);
287 if (-1 == qConnect(connectingSocket, (struct sockaddr *)&name, sizeof(name))) {
288 QString function = QLatin1String("QLocalSocket::connectToServer");
289 switch (errno)
290 {
291 case EINVAL:
292 case ECONNREFUSED:
293 errorOccurred(QLocalSocket::ConnectionRefusedError, function);
294 break;
295 case ENOENT:
296 errorOccurred(QLocalSocket::ServerNotFoundError, function);
297 break;
298 case EACCES:
299 case EPERM:
300 errorOccurred(QLocalSocket::SocketAccessError, function);
301 break;
302 case ETIMEDOUT:
303 errorOccurred(QLocalSocket::SocketTimeoutError, function);
304 break;
305 case EAGAIN:
306 // Try again later, all of the sockets listening are full
307 if (!delayConnect) {
308 delayConnect = new QSocketNotifier(connectingSocket, QSocketNotifier::Write);
309 q->connect(delayConnect, SIGNAL(activated(int)), q, SLOT(_q_connectToSocket()));
310 }
311 if (!connectTimer) {
312 connectTimer = new QTimer(q);
313 q->connect(connectTimer, SIGNAL(timeout()),
314 q, SLOT(_q_abortConnectionAttempt()),
315 Qt::DirectConnection);
316 connectTimer->start(QT_CONNECT_TIMEOUT);
317 }
318 delayConnect->setEnabled(true);
319 break;
320 default:
321 errorOccurred(QLocalSocket::UnknownSocketError, function);
322 }
323 return;
324 }
325
326 // connected!
327 if (delayConnect) {
328 delayConnect->setEnabled(false);
329 delete delayConnect;
330 delayConnect = 0;
331 }
332 serverName = connectingName;
333 fullServerName = connectingPathName;
334 if (unixSocket.setSocketDescriptor(connectingSocket,
335 QAbstractSocket::ConnectedState, connectingOpenMode)) {
336 q->QIODevice::open(connectingOpenMode);
337 q->emit connected();
338 } else {
339 QString function = QLatin1String("QLocalSocket::connectToServer");
340 errorOccurred(QLocalSocket::UnknownSocketError, function);
341 }
342 connectingSocket = -1;
343 connectingName = QString();
344 connectingOpenMode = 0;
345}
346
347bool QLocalSocket::setSocketDescriptor(quintptr socketDescriptor,
348 LocalSocketState socketState, OpenMode openMode)
349{
350 Q_D(QLocalSocket);
351 QAbstractSocket::SocketState newSocketState = QAbstractSocket::UnconnectedState;
352 switch (socketState) {
353 case ConnectingState:
354 newSocketState = QAbstractSocket::ConnectingState;
355 break;
356 case ConnectedState:
357 newSocketState = QAbstractSocket::ConnectedState;
358 break;
359 case ClosingState:
360 newSocketState = QAbstractSocket::ClosingState;
361 break;
362 case UnconnectedState:
363 newSocketState = QAbstractSocket::UnconnectedState;
364 break;
365 }
366 QIODevice::open(openMode);
367 d->state = socketState;
368 return d->unixSocket.setSocketDescriptor(socketDescriptor,
369 newSocketState, openMode);
370}
371
372void QLocalSocketPrivate::_q_abortConnectionAttempt()
373{
374 Q_Q(QLocalSocket);
375 q->close();
376}
377
378quintptr QLocalSocket::socketDescriptor() const
379{
380 Q_D(const QLocalSocket);
381 return d->unixSocket.socketDescriptor();
382}
383
384qint64 QLocalSocket::readData(char *data, qint64 c)
385{
386 Q_D(QLocalSocket);
387 return d->unixSocket.readData(data, c);
388}
389
390qint64 QLocalSocket::writeData(const char *data, qint64 c)
391{
392 Q_D(QLocalSocket);
393 return d->unixSocket.writeData(data, c);
394}
395
396void QLocalSocket::abort()
397{
398 Q_D(QLocalSocket);
399 d->unixSocket.abort();
400}
401
402qint64 QLocalSocket::bytesAvailable() const
403{
404 Q_D(const QLocalSocket);
405 return QIODevice::bytesAvailable() + d->unixSocket.bytesAvailable();
406}
407
408qint64 QLocalSocket::bytesToWrite() const
409{
410 Q_D(const QLocalSocket);
411 return d->unixSocket.bytesToWrite();
412}
413
414bool QLocalSocket::canReadLine() const
415{
416 Q_D(const QLocalSocket);
417 return QIODevice::canReadLine() || d->unixSocket.canReadLine();
418}
419
420void QLocalSocket::close()
421{
422 Q_D(QLocalSocket);
423 d->unixSocket.close();
424 if (d->delayConnect) {
425 d->delayConnect->setEnabled(false);
426 delete d->delayConnect;
427 d->delayConnect = 0;
428 d->connectTimer->stop();
429 delete d->connectTimer;
430 d->connectTimer = 0;
431 }
432 if (d->connectingSocket != -1)
433 ::close(d->connectingSocket);
434 d->connectingSocket = -1;
435 d->connectingName = QString();
436 d->connectingOpenMode = 0;
437 d->serverName = QString();
438 d->fullServerName = QString();
439 QIODevice::close();
440}
441
442bool QLocalSocket::waitForBytesWritten(int msecs)
443{
444 Q_D(QLocalSocket);
445 return d->unixSocket.waitForBytesWritten(msecs);
446}
447
448bool QLocalSocket::flush()
449{
450 Q_D(QLocalSocket);
451 return d->unixSocket.flush();
452}
453
454void QLocalSocket::disconnectFromServer()
455{
456 Q_D(QLocalSocket);
457 d->unixSocket.disconnectFromHost();
458}
459
460QLocalSocket::LocalSocketError QLocalSocket::error() const
461{
462 Q_D(const QLocalSocket);
463 switch (d->unixSocket.error()) {
464 case QAbstractSocket::ConnectionRefusedError:
465 return QLocalSocket::ConnectionRefusedError;
466 case QAbstractSocket::RemoteHostClosedError:
467 return QLocalSocket::PeerClosedError;
468 case QAbstractSocket::HostNotFoundError:
469 return QLocalSocket::ServerNotFoundError;
470 case QAbstractSocket::SocketAccessError:
471 return QLocalSocket::SocketAccessError;
472 case QAbstractSocket::SocketResourceError:
473 return QLocalSocket::SocketResourceError;
474 case QAbstractSocket::SocketTimeoutError:
475 return QLocalSocket::SocketTimeoutError;
476 case QAbstractSocket::DatagramTooLargeError:
477 return QLocalSocket::DatagramTooLargeError;
478 case QAbstractSocket::NetworkError:
479 return QLocalSocket::ConnectionError;
480 case QAbstractSocket::UnsupportedSocketOperationError:
481 return QLocalSocket::UnsupportedSocketOperationError;
482 case QAbstractSocket::UnknownSocketError:
483 return QLocalSocket::UnknownSocketError;
484 default:
485#if defined QLOCALSOCKET_DEBUG
486 qWarning() << "QLocalSocket error not handled:" << d->unixSocket.error();
487#endif
488 break;
489 }
490 return UnknownSocketError;
491}
492
493bool QLocalSocket::isValid() const
494{
495 Q_D(const QLocalSocket);
496 return d->unixSocket.isValid();
497}
498
499qint64 QLocalSocket::readBufferSize() const
500{
501 Q_D(const QLocalSocket);
502 return d->unixSocket.readBufferSize();
503}
504
505void QLocalSocket::setReadBufferSize(qint64 size)
506{
507 Q_D(QLocalSocket);
508 d->unixSocket.setReadBufferSize(size);
509}
510
511bool QLocalSocket::waitForConnected(int msec)
512{
513 Q_D(QLocalSocket);
514 if (state() != ConnectingState)
515 return (state() == ConnectedState);
516
517 fd_set readfds;
518 FD_ZERO(&readfds);
519 FD_SET(d->connectingSocket, &readfds);
520
521 timeval timeout;
522 timeout.tv_sec = msec / 1000;
523 timeout.tv_usec = (msec % 1000) * 1000;
524
525 // timeout can not be 0 or else select will return an error.
526 if (0 == msec)
527 timeout.tv_usec = 1000;
528
529 int result = -1;
530 // on Linux timeout will be updated by select, but _not_ on other systems.
531 QTime timer;
532 timer.start();
533 while (state() == ConnectingState
534 && (-1 == msec || timer.elapsed() < msec)) {
535 result = ::select(d->connectingSocket + 1, &readfds, 0, 0, &timeout);
536 if (-1 == result && errno != EINTR) {
537 d->errorOccurred( QLocalSocket::UnknownSocketError,
538 QLatin1String("QLocalSocket::waitForConnected"));
539 break;
540 }
541 if (result > 0)
542 d->_q_connectToSocket();
543 }
544
545 return (state() == ConnectedState);
546}
547
548bool QLocalSocket::waitForDisconnected(int msecs)
549{
550 Q_D(QLocalSocket);
551 if (state() == UnconnectedState) {
552 qWarning() << "QLocalSocket::waitForDisconnected() is not allowed in UnconnectedState";
553 return false;
554 }
555 return (d->unixSocket.waitForDisconnected(msecs));
556}
557
558bool QLocalSocket::waitForReadyRead(int msecs)
559{
560 Q_D(QLocalSocket);
561 if (state() == QLocalSocket::UnconnectedState)
562 return false;
563 return (d->unixSocket.waitForReadyRead(msecs));
564}
565
566QT_END_NAMESPACE
567
568#endif
Note: See TracBrowser for help on using the repository browser.