KDECore
klocalsocket_unix.cpp
Go to the documentation of this file.
00001 /* 00002 * This file is part of the KDE libraries 00003 * Copyright (C) 2007 Thiago Macieira <thiago@kde.org> 00004 * 00005 * This library is free software; you can redistribute it and/or 00006 * modify it under the terms of the GNU Library General Public 00007 * License as published by the Free Software Foundation; either 00008 * version 2 of the License, or (at your option) any later version. 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Library General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Library General Public License 00016 * along with this library; see the file COPYING.LIB. If not, write to 00017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 * Boston, MA 02110-1301, USA. 00019 */ 00020 #include <config-network.h> 00021 00022 #include <sys/types.h> 00023 #include <sys/time.h> 00024 #include <sys/socket.h> 00025 #include <sys/select.h> 00026 #include <sys/un.h> 00027 #include <errno.h> 00028 #include <fcntl.h> 00029 #include <string.h> 00030 #include <unistd.h> 00031 00032 #include "klocale.h" 00033 00034 static inline int kSocket(int af, int socketype, int proto) 00035 { 00036 int ret; 00037 do { 00038 ret = ::socket(af, socketype, proto); 00039 } while (ret == -1 && errno == EINTR); 00040 return ret; 00041 } 00042 00043 static inline int kBind(int fd, const sockaddr *sa, int len) 00044 { 00045 int ret; 00046 do { 00047 ret = ::bind(fd, sa, len); 00048 } while (ret == -1 && errno == EINTR); 00049 return ret; 00050 } 00051 00052 static inline int kConnect(int fd, const sockaddr *sa, int len) 00053 { 00054 int ret; 00055 do { 00056 ret = ::connect(fd, sa, len); 00057 } while (ret == -1 && errno == EINTR); 00058 return ret; 00059 } 00060 00061 static inline int kListen(int fd, int backlog) 00062 { 00063 int ret; 00064 do { 00065 ret = ::listen(fd, backlog); 00066 } while (ret == -1 && errno == EINTR); 00067 return ret; 00068 } 00069 00070 static inline int kAccept(int fd) 00071 { 00072 int ret; 00073 sockaddr sa; 00074 socklen_t len = sizeof(sa); 00075 do { 00076 ret = ::accept(fd, &sa, &len); 00077 } while (ret == -1 && errno == EINTR); 00078 return ret; 00079 } 00080 00081 #ifdef socket 00082 #undef socket 00083 #endif 00084 00085 #ifdef bind 00086 #undef bind 00087 #endif 00088 00089 #ifdef listen 00090 #undef listen 00091 #endif 00092 00093 #ifdef connect 00094 #undef connect 00095 #endif 00096 00097 #ifdef accept 00098 #undef accept 00099 #endif 00100 00101 #include <QtCore/qfile.h> 00102 #include <QtCore/qsocketnotifier.h> 00103 #include <QtCore/qvarlengtharray.h> 00104 00105 #include "klocalsocket.h" 00106 #include "klocalsocket_p.h" 00107 00108 #if !defined(AF_UNIX) && defined(AF_LOCAL) 00109 # define AF_UNIX AF_LOCAL 00110 #endif 00111 00112 class KSockaddrUn 00113 { 00114 int datalen; 00115 QVarLengthArray<char, 128> data; 00116 public: 00117 KSockaddrUn(const QString &path, KLocalSocket::LocalSocketType type); 00118 bool ok() const { return datalen; } 00119 int length() const { return datalen; } 00120 const sockaddr* address() 00121 { return reinterpret_cast<sockaddr *>(data.data()); } 00122 }; 00123 00124 KSockaddrUn::KSockaddrUn(const QString &path, KLocalSocket::LocalSocketType type) 00125 : datalen(0) 00126 { 00127 if (path.isEmpty()) 00128 return; 00129 00130 QString path2(path); 00131 if (!path.startsWith(QLatin1Char('/'))) 00132 // relative path; put everything in /tmp 00133 path2.prepend(QLatin1String("/tmp/")); 00134 00135 QByteArray encodedPath = QFile::encodeName(path2); 00136 00137 datalen = MIN_SOCKADDR_UN_LEN + encodedPath.length(); 00138 if (type == KLocalSocket::AbstractUnixSocket) 00139 ++datalen; 00140 data.resize(datalen); 00141 00142 sockaddr_un *saddr = reinterpret_cast<sockaddr_un *>(data.data()); 00143 saddr->sun_family = AF_UNIX; 00144 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN 00145 saddr->sun_len = datalen; 00146 #endif 00147 00148 if (type == KLocalSocket::UnixSocket) { 00149 strcpy(saddr->sun_path, encodedPath.constData()); 00150 } else if (type == KLocalSocket::AbstractUnixSocket) { 00151 *saddr->sun_path = '\0'; 00152 strcpy(saddr->sun_path + 1, encodedPath.constData()); 00153 } else { 00154 datalen = 0; // error 00155 } 00156 } 00157 00158 static bool setNonBlocking(int fd) 00159 { 00160 int fdflags = fcntl(fd, F_GETFL, 0); 00161 if (fdflags == -1) 00162 return false; // error 00163 00164 fdflags |= O_NONBLOCK; 00165 if (fcntl(fd, F_SETFL, fdflags) == -1) 00166 return false; // error 00167 00168 return true; 00169 } 00170 00171 void KLocalSocketPrivate::connectToPath(const QString &path, KLocalSocket::LocalSocketType aType, 00172 QAbstractSocket::OpenMode openMode) 00173 { 00174 if (aType == KLocalSocket::UnixSocket || aType == KLocalSocket::AbstractUnixSocket) { 00175 // connect to Unix socket 00176 KSockaddrUn addr(path, aType); 00177 if (!addr.ok()) { 00178 emitError(QAbstractSocket::NetworkError, i18n("Specified socket path is invalid")); 00179 return; 00180 } 00181 00182 // create the socket 00183 int fd = kSocket(AF_UNIX, SOCK_STREAM, 0); 00184 if (fd == -1) { 00185 // failed 00186 emitError(QAbstractSocket::UnsupportedSocketOperationError, 00187 i18n("The socket operation is not supported")); 00188 return; 00189 } 00190 00191 // try to connect 00192 // ### support non-blocking mode! 00193 if (kConnect(fd, addr.address(), addr.length()) == -1) { 00194 // failed 00195 int error = errno; 00196 ::close(fd); 00197 00198 switch (error) { 00199 case ECONNREFUSED: 00200 emitError(QAbstractSocket::ConnectionRefusedError, i18n("Connection refused")); 00201 return; 00202 00203 case EACCES: 00204 case EPERM: 00205 emitError(QAbstractSocket::SocketAccessError, i18n("Permission denied")); 00206 return; 00207 00208 case ETIMEDOUT: 00209 emitError(QAbstractSocket::SocketTimeoutError, i18n("Connection timed out")); 00210 return; 00211 00212 default: 00213 emitError(QAbstractSocket::UnknownSocketError, i18n("Unknown error")); 00214 return; 00215 } 00216 } 00217 00218 // if we got here, we succeeded in connecting 00219 if (!setNonBlocking(fd)) { 00220 ::close(fd); 00221 emitError(QAbstractSocket::UnknownSocketError, i18n("Could not set non-blocking mode")); 00222 return; 00223 } 00224 00225 // all is good 00226 peerPath = path; 00227 type = aType; 00228 00229 // setSocketDescriptor emits stateChanged 00230 q->setSocketDescriptor(fd, QAbstractSocket::ConnectedState, openMode); 00231 emit q->connected(); 00232 } else { 00233 emitError(QAbstractSocket::UnsupportedSocketOperationError, 00234 i18n("The socket operation is not supported")); 00235 } 00236 } 00237 00238 bool KLocalSocketServerPrivate::listen(const QString &path, KLocalSocket::LocalSocketType aType) 00239 { 00240 qDeleteAll(pendingConnections); 00241 pendingConnections.clear(); 00242 00243 if (aType == KLocalSocket::UnixSocket || aType == KLocalSocket::AbstractUnixSocket) { 00244 KSockaddrUn addr(path, aType); 00245 if (!addr.ok()) { 00246 emitError(QAbstractSocket::NetworkError, i18n("Specified socket path is invalid")); 00247 return false; 00248 } 00249 00250 // create the socket 00251 descriptor = kSocket(AF_UNIX, SOCK_STREAM, 0); 00252 if (descriptor == -1) { 00253 // failed 00254 emitError(QAbstractSocket::UnsupportedSocketOperationError, 00255 i18n("The socket operation is not supported")); 00256 return false; 00257 } 00258 00259 // try to bind to the address 00260 localPath = path; 00261 if (kBind(descriptor, addr.address(), addr.length()) == -1 || 00262 kListen(descriptor, 5) == -1) { 00263 int error = errno; 00264 close(); 00265 00266 switch (error) { 00267 case EACCES: 00268 emitError(QAbstractSocket::SocketAccessError, i18n("Permission denied")); 00269 return false; 00270 00271 case EADDRINUSE: 00272 emitError(QAbstractSocket::AddressInUseError, i18n("Address is already in use")); 00273 return false; 00274 00275 case ELOOP: 00276 case ENAMETOOLONG: 00277 emitError(QAbstractSocket::NetworkError, i18n("Path cannot be used")); 00278 return false; 00279 00280 case ENOENT: 00281 emitError(QAbstractSocket::HostNotFoundError, i18n("No such file or directory")); 00282 return false; 00283 00284 case ENOTDIR: 00285 emitError(QAbstractSocket::HostNotFoundError, i18n("Not a directory")); 00286 return false; 00287 00288 case EROFS: 00289 emitError(QAbstractSocket::SocketResourceError, i18n("Read-only filesystem")); 00290 return false; 00291 00292 default: 00293 emitError(QAbstractSocket::UnknownSocketError, i18n("Unknown error")); 00294 return false; 00295 } 00296 } 00297 00298 // if we got here, we succeeded in connecting 00299 if (!setNonBlocking(descriptor)) { 00300 close(); 00301 emitError(QAbstractSocket::UnknownSocketError, i18n("Could not set non-blocking mode")); 00302 return false; 00303 } 00304 00305 // done 00306 state = QAbstractSocket::ListeningState; 00307 type = aType; 00308 readNotifier = new QSocketNotifier(descriptor, QSocketNotifier::Read, q); 00309 readNotifier->setEnabled(maxPendingConnections > 0); 00310 QObject::connect(readNotifier, SIGNAL(activated(int)), 00311 q, SLOT(_k_newConnectionActivity())); 00312 return true; 00313 } 00314 00315 return false; 00316 } 00317 00318 void KLocalSocketServerPrivate::close() 00319 { 00320 if (descriptor != -1) 00321 ::close(descriptor); 00322 descriptor = -1; 00323 00324 delete readNotifier; 00325 readNotifier = 0; 00326 00327 if (type == KLocalSocket::UnixSocket) 00328 QFile::remove(localPath); 00329 localPath.clear(); 00330 type = KLocalSocket::UnknownLocalSocketType; 00331 00332 state = QAbstractSocket::UnconnectedState; 00333 error = QAbstractSocket::UnknownSocketError; 00334 errorString.clear(); 00335 } 00336 00337 bool KLocalSocketServerPrivate::waitForNewConnection(int msec, bool *timedOut) 00338 { 00339 timeval tv; 00340 tv.tv_sec = msec / 1000; 00341 tv.tv_usec = (msec % 1000) * 1000; 00342 00343 fd_set readset; 00344 FD_ZERO(&readset); 00345 FD_SET(descriptor, &readset); 00346 00347 while (descriptor != -1) { 00348 int code = ::select(descriptor + 1, &readset, 0, 0, &tv); 00349 if (code == -1 && errno == EINTR) { 00350 // interrupted 00351 continue; 00352 } else if (code == -1) { 00353 // error 00354 emitError(QAbstractSocket::UnknownSocketError, i18n("Unknown socket error")); 00355 close(); 00356 return false; 00357 } else if (code == 0) { 00358 // timed out 00359 if (timedOut) 00360 *timedOut = true; 00361 return false; 00362 } 00363 00364 // we must've got a connection. At least, there's activity. 00365 if (processSocketActivity()) { 00366 if (timedOut) 00367 *timedOut = false; 00368 return true; 00369 } 00370 } 00371 return false; 00372 } 00373 00374 bool KLocalSocketServerPrivate::processSocketActivity() 00375 { 00376 // we got a read notification in our socket 00377 // see if we can accept anything 00378 int newDescriptor = kAccept(descriptor); 00379 if (newDescriptor == -1) { 00380 switch (errno) { 00381 case EAGAIN: 00382 // shouldn't have happened, but it's ok 00383 return false; // no new socket 00384 00385 default: 00386 emitError(QAbstractSocket::UnknownSocketError, i18n("Unknown socket error")); 00387 // fall through 00388 } 00389 00390 close(); 00391 return false; 00392 } 00393 00394 q->incomingConnection(newDescriptor); 00395 readNotifier->setEnabled(pendingConnections.size() < maxPendingConnections); 00396 return true; 00397 } 00398 00399 void KLocalSocketServerPrivate::_k_newConnectionActivity() 00400 { 00401 if (descriptor == -1) 00402 return; 00403 00404 processSocketActivity(); 00405 }
KDE 4.6 API Reference