src/gui/embedded/qunixsocketserver.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qunixsocketserver_p.h"
       
    43 
       
    44 // #define QUNIXSOCKETSERVER_DEBUG
       
    45 
       
    46 #ifdef QUNIXSOCKETSERVER_DEBUG
       
    47 #include <QDebug>
       
    48 #endif
       
    49 
       
    50 #include <QtCore/qsocketnotifier.h>
       
    51 
       
    52 extern "C" {
       
    53 #include <sys/types.h>
       
    54 #include <sys/socket.h>
       
    55 #include <sys/un.h>
       
    56 #include <unistd.h>
       
    57 #include <errno.h>
       
    58 };
       
    59 
       
    60 #define UNIX_PATH_MAX 108 // From unix(7)
       
    61 
       
    62 QT_BEGIN_NAMESPACE
       
    63 
       
    64 class QUnixSocketServerPrivate : public QObject
       
    65 {
       
    66 Q_OBJECT
       
    67 public:
       
    68     QUnixSocketServerPrivate(QUnixSocketServer * parent)
       
    69     : QObject(), me(parent), fd(-1), maxConns(30),
       
    70       error(QUnixSocketServer::NoError), acceptNotifier(0)
       
    71     {}
       
    72 
       
    73     QUnixSocketServer * me;
       
    74     int fd;
       
    75     int maxConns;
       
    76     QByteArray address;
       
    77     QUnixSocketServer::ServerError error;
       
    78     QSocketNotifier * acceptNotifier;
       
    79 public slots:
       
    80     void acceptActivated();
       
    81 };
       
    82 
       
    83 /*!
       
    84   \class QUnixSocketServer
       
    85   \internal
       
    86 
       
    87   \brief The QUnixSocketServer class provides a Unix domain socket based server.
       
    88   \omit
       
    89   \ingroup Platform::DeviceSpecific
       
    90   \ingroup Platform::OS
       
    91   \ingroup Platform::Communications
       
    92   \endomit
       
    93   \ingroup qws
       
    94 
       
    95   This class makes it possible to accept incoming Unix domain socket
       
    96   connections.  Call \l QUnixSocketServer::listen() to have the server listen
       
    97   for incoming connections on a specified path.  The pure virtual
       
    98   \l QUnixSocketServer::incomingConnection() is called each time a new
       
    99   connection is established.  Users must inherit from QUnixSocketServer and
       
   100   implement this method.
       
   101 
       
   102   If an error occurs, \l QUnixSocketServer::serverError() returns the type of
       
   103   error.  Errors can only occur during server establishment - that is, during a
       
   104   call to \l QUnixSocketServer::listen().  Calling \l QUnixSocketServer::close()
       
   105   causes QUnixSocketServer to stop listening for connections and reset its
       
   106   state.
       
   107 
       
   108   QUnixSocketServer is often used in conjunction with the \l QUnixSocket class.
       
   109 
       
   110   \sa QUnixSocket
       
   111 */
       
   112 
       
   113 /*!
       
   114   \enum QUnixSocketServer::ServerError
       
   115 
       
   116   The ServerError enumeration represents the errors that can occur during server
       
   117   establishment.  The most recent error can be retrieved through a call to
       
   118   \l QUnixSocketServer::serverError().
       
   119 
       
   120   \value NoError No error has occurred.
       
   121   \value InvalidPath An invalid path endpoint was passed to
       
   122          \l QUnixSocketServer::listen().  As defined by unix(7), invalid paths
       
   123          include an empty path, or what more than 107 characters long.
       
   124   \value ResourceError An error acquiring or manipulating the system's socket
       
   125          resources occurred.  For example, if the process runs out of available
       
   126          socket descriptors, a ResourceError will occur.
       
   127   \value BindError The server was unable to bind to the specified path.
       
   128   \value ListenError The server was unable to listen on the specified path for
       
   129          incoming connections.
       
   130   */
       
   131 
       
   132 /*!
       
   133   Create a new Unix socket server with the given \a parent.
       
   134   */
       
   135 QUnixSocketServer::QUnixSocketServer(QObject *parent)
       
   136 : QObject(parent), d(0)
       
   137 {
       
   138 }
       
   139 
       
   140 /*!
       
   141   Stops listening for incoming connection and destroys the Unix socket server.
       
   142   */
       
   143 QUnixSocketServer::~QUnixSocketServer()
       
   144 {
       
   145     close();
       
   146     if(d)
       
   147         delete d;
       
   148 }
       
   149 
       
   150 /*!
       
   151   Stop listening for incoming connections and resets the Unix socket server's
       
   152   state.  Calling this method while \l {QUnixSocketServer::isListening()}{not listening } for incoming connections is a no-op.
       
   153 
       
   154   \sa QUnixSocketServer::listen()
       
   155   */
       
   156 void QUnixSocketServer::close()
       
   157 {
       
   158     if(!d)
       
   159         return;
       
   160 
       
   161     if(d->acceptNotifier) {
       
   162         d->acceptNotifier->setEnabled(false);
       
   163         delete d->acceptNotifier;
       
   164     }
       
   165     d->acceptNotifier = 0;
       
   166 
       
   167     if(-1 != d->fd) {
       
   168 #ifdef QUNIXSOCKET_DEBUG
       
   169         int closerv =
       
   170 #endif
       
   171             ::close(d->fd);
       
   172 #ifdef QUNIXSOCKET_DEBUG
       
   173         if(0 != closerv) {
       
   174             qDebug() << "QUnixSocketServer: Unable to close socket ("
       
   175                      << strerror(errno) << ')';
       
   176         }
       
   177 #endif
       
   178     }
       
   179     d->fd = -1;
       
   180     d->address = QByteArray();
       
   181     d->error = NoError;
       
   182 }
       
   183 
       
   184 /*!
       
   185   Returns the last server error.  Errors may only occur within a call to
       
   186   \l QUnixSocketServer::listen(), and only when such a call fails.
       
   187 
       
   188   This method is not destructive, so multiple calls to
       
   189   QUnixSocketServer::serverError() will return the same value.  The error is
       
   190   only reset by an explicit call to \l QUnixSocketServer::close() or
       
   191   by further calls to \l QUnixSocketServer::listen().
       
   192   */
       
   193 QUnixSocketServer::ServerError QUnixSocketServer::serverError() const
       
   194 {
       
   195     if(!d)
       
   196         return NoError;
       
   197 
       
   198     return d->error;
       
   199 }
       
   200 
       
   201 /*!
       
   202   Returns true if this server is listening for incoming connections, false
       
   203   otherwise.
       
   204 
       
   205   \sa QUnixSocketServer::listen()
       
   206   */
       
   207 bool QUnixSocketServer::isListening() const
       
   208 {
       
   209     if(!d)
       
   210         return false;
       
   211 
       
   212     return (-1 != d->fd);
       
   213 }
       
   214 
       
   215 /*!
       
   216   Tells the server to listen for incoming connections on \a path.  Returns true
       
   217   if it successfully initializes, false otherwise.  In the case of failure, the
       
   218   \l QUnixSocketServer::serverError() error status is set accordingly.
       
   219 
       
   220   Calling this method while the server is already running will result in the
       
   221   server begin reset, and then attempting to listen on \a path.  This will not
       
   222   affect connections established prior to the server being reset, but further
       
   223   incoming connections on the previous path will be refused.
       
   224 
       
   225   The server can be explicitly reset by a call to \l QUnixSocketServer::close().
       
   226 
       
   227   \sa QUnixSocketServer::close()
       
   228   */
       
   229 bool QUnixSocketServer::listen(const QByteArray & path)
       
   230 {
       
   231     if(d) {
       
   232         close(); // Any existing server is destroyed
       
   233     } else {
       
   234         d = new QUnixSocketServerPrivate(this);
       
   235     }
       
   236 
       
   237     if(path.isEmpty() || path.size() > UNIX_PATH_MAX) {
       
   238         d->error = InvalidPath;
       
   239         return false;
       
   240     }
       
   241     unlink( path );  // ok if this fails
       
   242 
       
   243     // Create the socket
       
   244     d->fd = ::socket(PF_UNIX, SOCK_STREAM, 0);
       
   245     if(-1 == d->fd) {
       
   246 #ifdef QUNIXSOCKETSERVER_DEBUG
       
   247         qDebug() << "QUnixSocketServer: Unable to create socket ("
       
   248                  << strerror(errno) << ')';
       
   249 #endif
       
   250         close();
       
   251         d->error = ResourceError;
       
   252         return false;
       
   253     }
       
   254 
       
   255     // Construct our unix address
       
   256     struct ::sockaddr_un addr;
       
   257     addr.sun_family = AF_UNIX;
       
   258     ::memcpy(addr.sun_path, path.data(), path.size());
       
   259     if(path.size() < UNIX_PATH_MAX)
       
   260         addr.sun_path[path.size()] = '\0';
       
   261 
       
   262     // Attempt to bind
       
   263     if(-1 == ::bind(d->fd, (sockaddr *)&addr, sizeof(sockaddr_un))) {
       
   264 #ifdef QUNIXSOCKETSERVER_DEBUG
       
   265         qDebug() << "QUnixSocketServer: Unable to bind socket ("
       
   266                  << strerror(errno) << ')';
       
   267 #endif
       
   268         close();
       
   269         d->error = BindError;
       
   270         return false;
       
   271     }
       
   272 
       
   273     // Listen to socket
       
   274     if(-1 == ::listen(d->fd, d->maxConns)) {
       
   275 #ifdef QUNIXSOCKETSERVER_DEBUG
       
   276         qDebug() << "QUnixSocketServer: Unable to listen socket ("
       
   277                  << strerror(errno) << ')';
       
   278 #endif
       
   279         close();
       
   280         d->error = ListenError;
       
   281         return false;
       
   282     }
       
   283 
       
   284     // Success!
       
   285     d->address = path;
       
   286     d->acceptNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, d);
       
   287     d->acceptNotifier->setEnabled(true);
       
   288     QObject::connect(d->acceptNotifier, SIGNAL(activated(int)),
       
   289                      d, SLOT(acceptActivated()));
       
   290 
       
   291     return true;
       
   292 }
       
   293 
       
   294 /*!
       
   295   Returns the Unix path on which this server is listening.  If this server is
       
   296   not listening, and empty address will be returned.
       
   297   */
       
   298 QByteArray QUnixSocketServer::serverAddress() const
       
   299 {
       
   300     if(!d)
       
   301         return QByteArray();
       
   302     return d->address;
       
   303 }
       
   304 
       
   305 int QUnixSocketServer::socketDescriptor() const
       
   306 {
       
   307     if (!d)
       
   308         return -1;
       
   309     return d->fd;
       
   310 }
       
   311 
       
   312 
       
   313 /*!
       
   314   Returns the maximum length the queue of pending connections may grow to.  That
       
   315   is, the maximum number of clients attempting to connect for which the Unix
       
   316   socket server has not yet accepted and passed to
       
   317   \l QUnixSocketServer::incomingConnection().  If a connection request arrives
       
   318   with the queue full, the client may receive a connection refused notification.
       
   319 
       
   320   By default a queue length of 30 is used.
       
   321 
       
   322   \sa QUnixSocketServer::setMaxPendingConnections()
       
   323   */
       
   324 int QUnixSocketServer::maxPendingConnections() const
       
   325 {
       
   326     if(!d)
       
   327         return 30;
       
   328 
       
   329     return d->maxConns;
       
   330 }
       
   331 
       
   332 /*!
       
   333   Sets the maximum length the queue of pending connections may grow to
       
   334   \a numConnections.  This value will only apply to
       
   335   \l QUnixSocketServer::listen() calls made following the value change - it will
       
   336   not be retroactively applied.
       
   337 
       
   338   \sa QUnixSocketServer::maxPendingConnections()
       
   339   */
       
   340 void QUnixSocketServer::setMaxPendingConnections(int numConnections)
       
   341 {
       
   342     Q_ASSERT(numConnections >= 1);
       
   343     if(!d)
       
   344         d = new QUnixSocketServerPrivate(this);
       
   345 
       
   346     d->maxConns = numConnections;
       
   347 }
       
   348 
       
   349 /*!
       
   350   \fn void QUnixSocketServer::incomingConnection(int socketDescriptor)
       
   351 
       
   352   This method is invoked each time a new incoming connection is established with
       
   353   the server.  Clients must reimplement this function in their QUnixSocketServer
       
   354   derived class to handle the connection.
       
   355 
       
   356   A common approach to handling the connection is to pass \a socketDescriptor to
       
   357   a QUnixSocket instance.
       
   358 
       
   359   \sa QUnixSocket
       
   360   */
       
   361 
       
   362 void QUnixSocketServerPrivate::acceptActivated()
       
   363 {
       
   364     ::sockaddr_un r;
       
   365     socklen_t len = sizeof(sockaddr_un);
       
   366     int connsock = ::accept(fd, (sockaddr *)&r, &len);
       
   367 #ifdef QUNIXSOCKETSERVER_DEBUG
       
   368     qDebug() << "QUnixSocketServer: Accept connection " << connsock;
       
   369 #endif
       
   370     if(-1 != connsock)
       
   371         me->incomingConnection(connsock);
       
   372 }
       
   373 
       
   374 QT_END_NAMESPACE
       
   375 
       
   376 #include "qunixsocketserver.moc"