• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

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

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal