| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 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$ |
| ** GNU Lesser General Public License Usage |
| ** 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. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $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" |