src/gui/embedded/qunixsocketserver.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/embedded/qunixsocketserver.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,376 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qunixsocketserver_p.h"
+
+// #define QUNIXSOCKETSERVER_DEBUG
+
+#ifdef QUNIXSOCKETSERVER_DEBUG
+#include <QDebug>
+#endif
+
+#include <QtCore/qsocketnotifier.h>
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+};
+
+#define UNIX_PATH_MAX 108 // From unix(7)
+
+QT_BEGIN_NAMESPACE
+
+class QUnixSocketServerPrivate : public QObject
+{
+Q_OBJECT
+public:
+    QUnixSocketServerPrivate(QUnixSocketServer * parent)
+    : QObject(), me(parent), fd(-1), maxConns(30),
+      error(QUnixSocketServer::NoError), acceptNotifier(0)
+    {}
+
+    QUnixSocketServer * me;
+    int fd;
+    int maxConns;
+    QByteArray address;
+    QUnixSocketServer::ServerError error;
+    QSocketNotifier * acceptNotifier;
+public slots:
+    void acceptActivated();
+};
+
+/*!
+  \class QUnixSocketServer
+  \internal
+
+  \brief The QUnixSocketServer class provides a Unix domain socket based server.
+  \omit
+  \ingroup Platform::DeviceSpecific
+  \ingroup Platform::OS
+  \ingroup Platform::Communications
+  \endomit
+  \ingroup qws
+
+  This class makes it possible to accept incoming Unix domain socket
+  connections.  Call \l QUnixSocketServer::listen() to have the server listen
+  for incoming connections on a specified path.  The pure virtual
+  \l QUnixSocketServer::incomingConnection() is called each time a new
+  connection is established.  Users must inherit from QUnixSocketServer and
+  implement this method.
+
+  If an error occurs, \l QUnixSocketServer::serverError() returns the type of
+  error.  Errors can only occur during server establishment - that is, during a
+  call to \l QUnixSocketServer::listen().  Calling \l QUnixSocketServer::close()
+  causes QUnixSocketServer to stop listening for connections and reset its
+  state.
+
+  QUnixSocketServer is often used in conjunction with the \l QUnixSocket class.
+
+  \sa QUnixSocket
+*/
+
+/*!
+  \enum QUnixSocketServer::ServerError
+
+  The ServerError enumeration represents the errors that can occur during server
+  establishment.  The most recent error can be retrieved through a call to
+  \l QUnixSocketServer::serverError().
+
+  \value NoError No error has occurred.
+  \value InvalidPath An invalid path endpoint was passed to
+         \l QUnixSocketServer::listen().  As defined by unix(7), invalid paths
+         include an empty path, or what more than 107 characters long.
+  \value ResourceError An error acquiring or manipulating the system's socket
+         resources occurred.  For example, if the process runs out of available
+         socket descriptors, a ResourceError will occur.
+  \value BindError The server was unable to bind to the specified path.
+  \value ListenError The server was unable to listen on the specified path for
+         incoming connections.
+  */
+
+/*!
+  Create a new Unix socket server with the given \a parent.
+  */
+QUnixSocketServer::QUnixSocketServer(QObject *parent)
+: QObject(parent), d(0)
+{
+}
+
+/*!
+  Stops listening for incoming connection and destroys the Unix socket server.
+  */
+QUnixSocketServer::~QUnixSocketServer()
+{
+    close();
+    if(d)
+        delete d;
+}
+
+/*!
+  Stop listening for incoming connections and resets the Unix socket server's
+  state.  Calling this method while \l {QUnixSocketServer::isListening()}{not listening } for incoming connections is a no-op.
+
+  \sa QUnixSocketServer::listen()
+  */
+void QUnixSocketServer::close()
+{
+    if(!d)
+        return;
+
+    if(d->acceptNotifier) {
+        d->acceptNotifier->setEnabled(false);
+        delete d->acceptNotifier;
+    }
+    d->acceptNotifier = 0;
+
+    if(-1 != d->fd) {
+#ifdef QUNIXSOCKET_DEBUG
+        int closerv =
+#endif
+            ::close(d->fd);
+#ifdef QUNIXSOCKET_DEBUG
+        if(0 != closerv) {
+            qDebug() << "QUnixSocketServer: Unable to close socket ("
+                     << strerror(errno) << ')';
+        }
+#endif
+    }
+    d->fd = -1;
+    d->address = QByteArray();
+    d->error = NoError;
+}
+
+/*!
+  Returns the last server error.  Errors may only occur within a call to
+  \l QUnixSocketServer::listen(), and only when such a call fails.
+
+  This method is not destructive, so multiple calls to
+  QUnixSocketServer::serverError() will return the same value.  The error is
+  only reset by an explicit call to \l QUnixSocketServer::close() or
+  by further calls to \l QUnixSocketServer::listen().
+  */
+QUnixSocketServer::ServerError QUnixSocketServer::serverError() const
+{
+    if(!d)
+        return NoError;
+
+    return d->error;
+}
+
+/*!
+  Returns true if this server is listening for incoming connections, false
+  otherwise.
+
+  \sa QUnixSocketServer::listen()
+  */
+bool QUnixSocketServer::isListening() const
+{
+    if(!d)
+        return false;
+
+    return (-1 != d->fd);
+}
+
+/*!
+  Tells the server to listen for incoming connections on \a path.  Returns true
+  if it successfully initializes, false otherwise.  In the case of failure, the
+  \l QUnixSocketServer::serverError() error status is set accordingly.
+
+  Calling this method while the server is already running will result in the
+  server begin reset, and then attempting to listen on \a path.  This will not
+  affect connections established prior to the server being reset, but further
+  incoming connections on the previous path will be refused.
+
+  The server can be explicitly reset by a call to \l QUnixSocketServer::close().
+
+  \sa QUnixSocketServer::close()
+  */
+bool QUnixSocketServer::listen(const QByteArray & path)
+{
+    if(d) {
+        close(); // Any existing server is destroyed
+    } else {
+        d = new QUnixSocketServerPrivate(this);
+    }
+
+    if(path.isEmpty() || path.size() > UNIX_PATH_MAX) {
+        d->error = InvalidPath;
+        return false;
+    }
+    unlink( path );  // ok if this fails
+
+    // Create the socket
+    d->fd = ::socket(PF_UNIX, SOCK_STREAM, 0);
+    if(-1 == d->fd) {
+#ifdef QUNIXSOCKETSERVER_DEBUG
+        qDebug() << "QUnixSocketServer: Unable to create socket ("
+                 << strerror(errno) << ')';
+#endif
+        close();
+        d->error = ResourceError;
+        return false;
+    }
+
+    // Construct our unix address
+    struct ::sockaddr_un addr;
+    addr.sun_family = AF_UNIX;
+    ::memcpy(addr.sun_path, path.data(), path.size());
+    if(path.size() < UNIX_PATH_MAX)
+        addr.sun_path[path.size()] = '\0';
+
+    // Attempt to bind
+    if(-1 == ::bind(d->fd, (sockaddr *)&addr, sizeof(sockaddr_un))) {
+#ifdef QUNIXSOCKETSERVER_DEBUG
+        qDebug() << "QUnixSocketServer: Unable to bind socket ("
+                 << strerror(errno) << ')';
+#endif
+        close();
+        d->error = BindError;
+        return false;
+    }
+
+    // Listen to socket
+    if(-1 == ::listen(d->fd, d->maxConns)) {
+#ifdef QUNIXSOCKETSERVER_DEBUG
+        qDebug() << "QUnixSocketServer: Unable to listen socket ("
+                 << strerror(errno) << ')';
+#endif
+        close();
+        d->error = ListenError;
+        return false;
+    }
+
+    // Success!
+    d->address = path;
+    d->acceptNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, d);
+    d->acceptNotifier->setEnabled(true);
+    QObject::connect(d->acceptNotifier, SIGNAL(activated(int)),
+                     d, SLOT(acceptActivated()));
+
+    return true;
+}
+
+/*!
+  Returns the Unix path on which this server is listening.  If this server is
+  not listening, and empty address will be returned.
+  */
+QByteArray QUnixSocketServer::serverAddress() const
+{
+    if(!d)
+        return QByteArray();
+    return d->address;
+}
+
+int QUnixSocketServer::socketDescriptor() const
+{
+    if (!d)
+        return -1;
+    return d->fd;
+}
+
+
+/*!
+  Returns the maximum length the queue of pending connections may grow to.  That
+  is, the maximum number of clients attempting to connect for which the Unix
+  socket server has not yet accepted and passed to
+  \l QUnixSocketServer::incomingConnection().  If a connection request arrives
+  with the queue full, the client may receive a connection refused notification.
+
+  By default a queue length of 30 is used.
+
+  \sa QUnixSocketServer::setMaxPendingConnections()
+  */
+int QUnixSocketServer::maxPendingConnections() const
+{
+    if(!d)
+        return 30;
+
+    return d->maxConns;
+}
+
+/*!
+  Sets the maximum length the queue of pending connections may grow to
+  \a numConnections.  This value will only apply to
+  \l QUnixSocketServer::listen() calls made following the value change - it will
+  not be retroactively applied.
+
+  \sa QUnixSocketServer::maxPendingConnections()
+  */
+void QUnixSocketServer::setMaxPendingConnections(int numConnections)
+{
+    Q_ASSERT(numConnections >= 1);
+    if(!d)
+        d = new QUnixSocketServerPrivate(this);
+
+    d->maxConns = numConnections;
+}
+
+/*!
+  \fn void QUnixSocketServer::incomingConnection(int socketDescriptor)
+
+  This method is invoked each time a new incoming connection is established with
+  the server.  Clients must reimplement this function in their QUnixSocketServer
+  derived class to handle the connection.
+
+  A common approach to handling the connection is to pass \a socketDescriptor to
+  a QUnixSocket instance.
+
+  \sa QUnixSocket
+  */
+
+void QUnixSocketServerPrivate::acceptActivated()
+{
+    ::sockaddr_un r;
+    socklen_t len = sizeof(sockaddr_un);
+    int connsock = ::accept(fd, (sockaddr *)&r, &len);
+#ifdef QUNIXSOCKETSERVER_DEBUG
+    qDebug() << "QUnixSocketServer: Accept connection " << connsock;
+#endif
+    if(-1 != connsock)
+        me->incomingConnection(connsock);
+}
+
+QT_END_NAMESPACE
+
+#include "qunixsocketserver.moc"