source: trunk/src/network/socket/qlocalserver_os2.cpp

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

network: Fixed: QLocalServer could hang forever in the destructor trying to close the listen socket being watched by a socket notifier. This fixes the Assistant application hang at startup.

File size: 7.4 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 "qlocalserver.h"
45#include "qlocalserver_p.h"
46#include "qlocalsocket.h"
47#include "qlocalsocket_p.h"
48
49#ifndef QT_NO_LOCALSERVER
50
51#include <sys/socket.h>
52#include <sys/un.h>
53
54#include <qdebug.h>
55#include <qdir.h>
56#include <qdatetime.h>
57
58QT_BEGIN_NAMESPACE
59
60void QLocalServerPrivate::init()
61{
62}
63
64bool QLocalServerPrivate::removeServer(const QString &name)
65{
66 Q_UNUSED(name);
67 return true;
68}
69
70bool QLocalServerPrivate::listen(const QString &requestedServerName)
71{
72 Q_Q(QLocalServer);
73
74 // Local OS/2 sockets must always start with "\socket\"
75 const QLatin1String socketPath("\\socket\\");
76
77 // determine the full server path
78 fullServerName = QDir::toNativeSeparators(requestedServerName);
79 if (!fullServerName.startsWith(socketPath)) {
80 fullServerName = socketPath + requestedServerName;
81 }
82 serverName = requestedServerName;
83
84 // create the unix socket
85 listenSocket = ::socket(PF_UNIX, SOCK_STREAM, 0);
86 if (-1 == listenSocket) {
87 setError(QLatin1String("QLocalServer::listen"));
88 closeServer();
89 return false;
90 }
91
92 // Construct the unix address
93 struct ::sockaddr_un addr;
94 addr.sun_family = PF_UNIX;
95 addr.sun_len = sizeof(sockaddr_un);
96 QByteArray fsn = fullServerName.toLocal8Bit();
97 if (sizeof(addr.sun_path) < (uint)fsn.size() + 1) {
98 errno = E2BIG; // indicate buffer overflow
99 setError(QLatin1String("QLocalServer::listen"));
100 closeServer();
101 return false;
102 }
103 ::strcpy(addr.sun_path, fsn);
104
105 // bind
106 if(-1 == ::bind(listenSocket, (sockaddr *)&addr, sizeof(sockaddr_un))) {
107 setError(QLatin1String("QLocalServer::listen"));
108 // if address is in use already, just close the socket, but do not delete the file
109 if(errno == EADDRINUSE)
110 QT_CLOSE(listenSocket);
111 // otherwise, close the socket and delete the file
112 else
113 closeServer();
114 listenSocket = -1;
115 return false;
116 }
117
118 // listen for connections
119 if (-1 == ::listen(listenSocket, 50)) {
120 setError(QLatin1String("QLocalServer::listen"));
121 closeServer();
122 listenSocket = -1;
123 return false;
124 }
125 Q_ASSERT(!socketNotifier);
126 socketNotifier = new QSocketNotifier(listenSocket,
127 QSocketNotifier::Read, q);
128 q->connect(socketNotifier, SIGNAL(activated(int)),
129 q, SLOT(_q_onNewConnection()));
130 socketNotifier->setEnabled(maxPendingConnections > 0);
131 return true;
132}
133
134void QLocalServerPrivate::closeServer()
135{
136 if (-1 != listenSocket) {
137 // stop select()ing on this socket, otherwise close() will hang
138 socketNotifier->setEnabled(false);
139 QT_CLOSE(listenSocket);
140 listenSocket = -1;
141 }
142
143 if (socketNotifier) {
144 socketNotifier->setEnabled(false); // Otherwise, closed socket is checked before deleter runs
145 socketNotifier->deleteLater();
146 socketNotifier = 0;
147 }
148}
149
150void QLocalServerPrivate::_q_onNewConnection()
151{
152 Q_Q(QLocalServer);
153 if (-1 == listenSocket)
154 return;
155
156 ::sockaddr_un addr;
157 int length = sizeof(sockaddr_un);
158 int connectedSocket = ::accept(listenSocket, (sockaddr *)&addr, &length);
159 if(-1 == connectedSocket) {
160 setError(QLatin1String("QLocalSocket::activated"));
161 closeServer();
162 } else {
163 socketNotifier->setEnabled(pendingConnections.size()
164 <= maxPendingConnections);
165 q->incomingConnection(connectedSocket);
166 }
167}
168
169void QLocalServerPrivate::waitForNewConnection(int msec, bool *timedOut)
170{
171 fd_set readfds;
172 FD_ZERO(&readfds);
173 FD_SET(listenSocket, &readfds);
174
175 timeval timeout;
176 timeout.tv_sec = msec / 1000;
177 timeout.tv_usec = (msec % 1000) * 1000;
178
179 int result = -1;
180 result = ::select(listenSocket + 1, &readfds, 0, 0, (msec == -1) ? 0 : &timeout);
181 if (-1 == result) {
182 setError(QLatin1String("QLocalServer::waitForNewConnection"));
183 closeServer();
184 }
185 if (result > 0)
186 _q_onNewConnection();
187 if (timedOut)
188 *timedOut = (result == 0);
189}
190
191void QLocalServerPrivate::setError(const QString &function)
192{
193 if (EAGAIN == errno)
194 return;
195
196 switch (errno) {
197 case EACCES:
198 errorString = QLocalServer::tr("%1: Permission denied").arg(function);
199 error = QAbstractSocket::SocketAccessError;
200 break;
201 case ELOOP:
202 case ENOENT:
203 case ENAMETOOLONG:
204 case EROFS:
205 case ENOTDIR:
206 errorString = QLocalServer::tr("%1: Name error").arg(function);
207 error = QAbstractSocket::HostNotFoundError;
208 break;
209 case EADDRINUSE:
210 errorString = QLocalServer::tr("%1: Address in use").arg(function);
211 error = QAbstractSocket::AddressInUseError;
212 break;
213
214 default:
215 errorString = QLocalServer::tr("%1: Unknown error %2")
216 .arg(function).arg(errno);
217 const char *err = strerror(errno);
218 if (err)
219 errorString += QString(QLatin1String(" (%1)")).arg(QLatin1String(err));
220 error = QAbstractSocket::UnknownSocketError;
221#if defined QLOCALSERVER_DEBUG
222 qWarning() << errorString << "fullServerName:" << fullServerName;
223#endif
224 }
225}
226
227QT_END_NAMESPACE
228
229#endif // QT_NO_LOCALSERVER
Note: See TracBrowser for help on using the repository browser.