/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** 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, Digia gives you certain additional ** rights. These rights are described in the Digia 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. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qunixsocket_p.h" // #define QUNIXSOCKET_DEBUG 1 #include #include #include #include "private/qcore_unix_p.h" // overrides QT_OPEN #ifdef QUNIXSOCKET_DEBUG #include #endif extern "C" { #include #include #include #include #include }; #define UNIX_PATH_MAX 108 // From unix(7) #ifdef QT_LINUXBASE // LSB doesn't declare ucred struct ucred { pid_t pid; /* PID of sending process. */ uid_t uid; /* UID of sending process. */ gid_t gid; /* GID of sending process. */ }; // LSB doesn't define the ones below #ifndef SO_PASSCRED # define SO_PASSCRED 16 #endif #ifndef SCM_CREDENTIALS # define SCM_CREDENTIALS 0x02 #endif #ifndef MSG_DONTWAIT # define MSG_DONTWAIT 0x40 #endif #ifndef MSG_NOSIGNAL # define MSG_NOSIGNAL 0x4000 #endif #endif // QT_LINUXBASE QT_BEGIN_NAMESPACE /////////////////////////////////////////////////////////////////////////////// // class QUnixSocketRights /////////////////////////////////////////////////////////////////////////////// /*! \class QUnixSocketRights \internal \brief The QUnixSocketRights class encapsulates QUnixSocket rights data. \omit \ingroup Platform::DeviceSpecific \ingroup Platform::OS \ingroup Platform::Communications \endomit \ingroup qws \l QUnixSocket allows you to transfer Unix file descriptors between processes. A file descriptor is referred to as "rights data" as it allows one process to transfer its right to access a resource to another. The Unix system verifies resource permissions only when the resource is first opened. For example, consider a file on disk readable only by the user "qt". A process running as user "qt" will be able to open this file for reading. If, while the process was still reading from the file, the ownership was changed from user "qt" to user "root", the process would be allowed to continue reading from the file, even though attempting to reopen the file would be denied. Permissions are associated with special descriptors called file descriptors which are returned to a process after it initially opens a resource. File descriptors can be duplicated within a process through the dup(2) system call. File descriptors can be passed between processes using the \l QUnixSocket class in the same way. Even though the receiving process never opened the resource directly, it has the same permissions to access it as the process that did. \sa QUnixSocket */ struct QUnixSocketRightsPrivate : public QSharedData { virtual ~QUnixSocketRightsPrivate() { #ifdef QUNIXSOCKET_DEBUG int closerv = #endif QT_CLOSE(fd); #ifdef QUNIXSOCKET_DEBUG if(0 != closerv) { qDebug() << "QUnixSocketRightsPrivate: Unable to close managed" " file descriptor (" << ::strerror(errno) << ')'; } #endif } int fd; }; /*! Create a new QUnixSocketRights instance containing the file descriptor \a fd. \a fd will be dup(2)'d internally, so the application is free to close \a fd following this call. If the dup(2) fails, or you pass an invalid \a fd, an \l {QUnixSocketRights::isValid()}{invalid } object will be constructed. QUnixSocketRights instances are immutable and the internal file descriptor will be shared between any copies made of this object. The system will close(2) the file descriptor once it is no longer needed. */ QUnixSocketRights::QUnixSocketRights(int fd) { d = new QUnixSocketRightsPrivate(); if(-1 == fd) { d->fd = -1; } else { d->fd = qt_safe_dup(fd); #ifdef QUNIXSOCKET_DEBUG if(-1 == d->fd) { qDebug() << "QUnixSocketRights: Unable to duplicate fd " << fd << " (" << ::strerror(errno) << ')'; } #endif } } /*! \internal Construct a QUnixSocketRights instance on \a fd without dup(2)'ing the file descriptor. */ QUnixSocketRights::QUnixSocketRights(int fd,int) { Q_ASSERT(-1 != fd); d = new QUnixSocketRightsPrivate(); d->fd = fd; } /*! Destroys the QUnixSocketRights instance. */ QUnixSocketRights::~QUnixSocketRights() { } /*! Create a copy of \a other. */ QUnixSocketRights & QUnixSocketRights::operator=(const QUnixSocketRights & other) { d = other.d; return *this; } /*! Create a copy of \a other. */ QUnixSocketRights::QUnixSocketRights(const QUnixSocketRights & other) : d(other.d) { } /*! Returns true if this QUnixSocketRights instance is managing a valid file descriptor. This method is equivalent to (-1 != peekFd()). \sa QUnixSocketRights::peekFd() */ bool QUnixSocketRights::isValid() const { return d->fd != -1; } /*! Return a duplicate of the file descriptor contained in this object. If this is an \l {QUnixSocketRights::isValid()}{invalid } object, or the dup(2) call fails, an invalid file descriptor (-1) will be returned. \sa QUnixSocketRights::peekFd() */ int QUnixSocketRights::dupFd() const { if(-1 == d->fd) return -1; int rv = qt_safe_dup(d->fd); #ifdef QUNIXSOCKET_DEBUG if(-1 == rv) qDebug() << "QUnixSocketRights: Unable to duplicate managed file " "descriptor (" << ::strerror(errno) << ')'; #endif return rv; } /*! Returns the file descriptor contained in this object. If this is an \l {QUnixSocketRights::isValid()}{invalid } object an invalid file descriptor (-1) will be returned. The lifetime of this file descriptor is tied to the lifetime of the QUnixSocketRights instance. The file descriptor returned by this method \e may be close(2)'d when the QUnixSocketRights instance is destroyed. If you want to continue to use the file descriptor use \l QUnixSocketRights::dupFd() instead. \sa QUnixSocketRights::dupFd() */ int QUnixSocketRights::peekFd() const { return d->fd; } /////////////////////////////////////////////////////////////////////////////// // class QUnixSocketMessage /////////////////////////////////////////////////////////////////////////////// struct QUnixSocketMessagePrivate : public QSharedData { QUnixSocketMessagePrivate() : state(Default), vec(0), iovecLen(0), dataSize(0) {} QUnixSocketMessagePrivate(const QByteArray & b) : bytes(b), state(Default), vec(0), iovecLen(0), dataSize(0) {} QUnixSocketMessagePrivate(const QByteArray & b, const QList & r) : bytes(b), rights(r), state(Default), vec(0), iovecLen(0), dataSize(0) {} int size() const { return vec ? dataSize : bytes.size(); } void removeBytes( unsigned int ); QByteArray bytes; QList rights; enum AncillaryDataState { Default = 0x00, Truncated = 0x01, Credential = 0x02 }; AncillaryDataState state; pid_t pid; gid_t gid; uid_t uid; ::iovec *vec; int iovecLen; // number of vectors in array int dataSize; // total size of vectors = payload }; /*! \internal Remove \a bytesToDequeue bytes from the front of this message */ void QUnixSocketMessagePrivate::removeBytes( unsigned int bytesToDequeue ) { if ( vec ) { ::iovec *vecPtr = vec; if ( bytesToDequeue > (unsigned int)dataSize ) bytesToDequeue = dataSize; while ( bytesToDequeue > 0 && iovecLen > 0 ) { if ( vecPtr->iov_len > bytesToDequeue ) { // dequeue the bytes by taking them off the front of the // current vector. since we don't own the iovec, its okay // to "leak" this away by pointing past it char **base = reinterpret_cast(&(vecPtr->iov_base)); *base += bytesToDequeue; vecPtr->iov_len -= bytesToDequeue; bytesToDequeue = 0; } else { // dequeue bytes by skipping a whole vector. again, its ok // to lose the pointers to this data bytesToDequeue -= vecPtr->iov_len; iovecLen--; vecPtr++; } } dataSize -= bytesToDequeue; if ( iovecLen == 0 ) vec = 0; } else { bytes.remove(0, bytesToDequeue ); } } /*! \class QUnixSocketMessage \internal \brief The QUnixSocketMessage class encapsulates a message sent or received through the QUnixSocket class. \omit \ingroup Platform::DeviceSpecific \ingroup Platform::OS \ingroup Platform::Communications \endomit \ingroup qws In addition to transmitting regular byte stream data, messages sent over Unix domain sockets may have special ancillary properties. QUnixSocketMessage instances allow programmers to retrieve and control these properties. Every QUnixSocketMessage sent has an associated set of credentials. A message's credentials consist of the process id, the user id and the group id of the sending process. Normally these credentials are set automatically for you by the QUnixSocketMessage class and can be queried by the receiving process using the \l QUnixSocketMessage::processId(), \l QUnixSocketMessage::userId() and \l QUnixSocketMessage::groupId() methods respectively. Advanced applications may wish to change the credentials that their message is sent with, and may do so though the \l QUnixSocketMessage::setProcessId(), \l QUnixSocketMessage::setUserId() and \l QUnixSocketMessage::setGroupId() methods. The validity of these credentials is verified by the system kernel. Only the root user can send messages with credentials that are not his own. Sending of the message will fail for any non-root user who attempts to fabricate credentials. Note that this failure is enforced by the system kernel - receivers can trust the accuracy of credential data! Unix domain socket messages may also be used to transmit Unix file descriptors between processes. In this context, file descriptors are known as rights data and are encapsulated by the \l QUnixSocketRights class. Senders can set the file descriptors to transmit using the \l QUnixSocketMessage::setRights() and receivers can retrieve this data through a call to \l QUnixSocketMessage::rights(). \l QUnixSocket and \l QUnixSocketRights discuss the specific copy and ordering semantic associated with rights data. QUnixSocketMessage messages are sent by the \l QUnixSocket::write() method. Like any normal network message, attempting to transmit an empty QUnixSocketMessage will succeed, but result in a no-op. Limitations in the Unix domain protocol semantic will cause a transmission of a QUnixSocketMessage with rights data, but no byte data portion, to fail. \sa QUnixSocket QUnixSocketRights */ /*! Construct an empty QUnixSocketMessage. This instance will have not data and no rights information. The message's credentials will be set to the application's default credentials. */ QUnixSocketMessage::QUnixSocketMessage() : d(new QUnixSocketMessagePrivate()) { } /*! Construct a QUnixSocketMessage with an initial data payload of \a bytes. The message's credentials will be set to the application's default credentials. */ QUnixSocketMessage::QUnixSocketMessage(const QByteArray & bytes) : d(new QUnixSocketMessagePrivate(bytes)) { } /*! Construct a QUnixSocketMessage with an initial data payload of \a bytes and an initial rights payload of \a rights. The message's credentials will be set to the application's default credentials. A message with rights data but an empty data payload cannot be transmitted by the system. */ QUnixSocketMessage::QUnixSocketMessage(const QByteArray & bytes, const QList & rights) : d(new QUnixSocketMessagePrivate(bytes, rights)) { } /*! Create a copy of \a other. */ QUnixSocketMessage::QUnixSocketMessage(const QUnixSocketMessage & other) : d(other.d) { } /*! \fn QUnixSocketMessage::QUnixSocketMessage(const iovec* data, int vecLen) Construct a QUnixSocketMessage with an initial data payload of \a data which points to an array of \a vecLen iovec structures. The message's credentials will be set to the application's default credentials. This method can be used to avoid the overhead of copying buffers of data and will directly send the data pointed to by \a data on the socket. It also avoids the syscall overhead of making a number of small socket write calls, if a number of data items can be delivered with one write. Caller must ensure the iovec * \a data remains valid until the message is flushed. Caller retains ownership of the iovec structs. */ QUnixSocketMessage::QUnixSocketMessage(const ::iovec* data, int vecLen ) : d(new QUnixSocketMessagePrivate()) { for ( int v = 0; v < vecLen; v++ ) d->dataSize += data[v].iov_len; d->vec = const_cast(data); d->iovecLen = vecLen; } /*! Assign the contents of \a other to this object. */ QUnixSocketMessage & QUnixSocketMessage::operator=(const QUnixSocketMessage & other) { d = other.d; return *this; } /*! Destroy this instance. */ QUnixSocketMessage::~QUnixSocketMessage() { } /*! Set the data portion of the message to \a bytes. \sa QUnixSocketMessage::bytes() */ void QUnixSocketMessage::setBytes(const QByteArray & bytes) { d.detach(); d->bytes = bytes; } /*! Set the rights portion of the message to \a rights. A message with rights data but an empty byte data payload cannot be transmitted by the system. \sa QUnixSocketMessage::rights() */ void QUnixSocketMessage::setRights(const QList & rights) { d.detach(); d->rights = rights; } /*! Return the rights portion of the message. \sa QUnixSocketMessage::setRights() */ const QList & QUnixSocketMessage::rights() const { return d->rights; } /*! Returns true if the rights portion of the message was truncated on reception due to insufficient buffer size. The rights buffer size can be adjusted through calls to the \l QUnixSocket::setRightsBufferSize() method. \l QUnixSocket contains a discussion of the buffering and truncation characteristics of the Unix domain protocol. \sa QUnixSocket QUnixSocket::setRightsBufferSize() */ bool QUnixSocketMessage::rightsWereTruncated() const { return d->state & QUnixSocketMessagePrivate::Truncated; } /*! Return the data portion of the message. \sa QUnixSocketMessage::setBytes() */ const QByteArray & QUnixSocketMessage::bytes() const { return d->bytes; } /*! Returns the process id credential associated with this message. \sa QUnixSocketMessage::setProcessId() */ pid_t QUnixSocketMessage::processId() const { if(QUnixSocketMessagePrivate::Credential & d->state) return d->pid; else return ::getpid(); } /*! Returns the user id credential associated with this message. \sa QUnixSocketMessage::setUserId() */ uid_t QUnixSocketMessage::userId() const { if(QUnixSocketMessagePrivate::Credential & d->state) return d->uid; else return ::geteuid(); } /*! Returns the group id credential associated with this message. \sa QUnixSocketMessage::setGroupId() */ gid_t QUnixSocketMessage::groupId() const { if(QUnixSocketMessagePrivate::Credential & d->state) return d->gid; else return ::getegid(); } /*! Set the process id credential associated with this message to \a pid. Unless you are the root user, setting a fraudulant credential will cause this message to fail. \sa QUnixSocketMessage::processId() */ void QUnixSocketMessage::setProcessId(pid_t pid) { if(!(d->state & QUnixSocketMessagePrivate::Credential)) { d->state = (QUnixSocketMessagePrivate::AncillaryDataState)( d->state | QUnixSocketMessagePrivate::Credential ); d->uid = ::geteuid(); d->gid = ::getegid(); } d->pid = pid; } /*! Set the user id credential associated with this message to \a uid. Unless you are the root user, setting a fraudulant credential will cause this message to fail. \sa QUnixSocketMessage::userId() */ void QUnixSocketMessage::setUserId(uid_t uid) { if(!(d->state & QUnixSocketMessagePrivate::Credential)) { d->state = (QUnixSocketMessagePrivate::AncillaryDataState)( d->state | QUnixSocketMessagePrivate::Credential ); d->pid = ::getpid(); d->gid = ::getegid(); } d->uid = uid; } /*! Set the group id credential associated with this message to \a gid. Unless you are the root user, setting a fraudulant credential will cause this message to fail. \sa QUnixSocketMessage::groupId() */ void QUnixSocketMessage::setGroupId(gid_t gid) { if(!(d->state & QUnixSocketMessagePrivate::Credential)) { d->state = (QUnixSocketMessagePrivate::AncillaryDataState)( d->state | QUnixSocketMessagePrivate::Credential ); d->pid = ::getpid(); d->uid = ::geteuid(); } d->gid = gid; } /*! Return true if this message is valid. A message with rights data but an empty byte data payload cannot be transmitted by the system and is marked as invalid. */ bool QUnixSocketMessage::isValid() const { return d->rights.isEmpty() || !d->bytes.isEmpty(); } /////////////////////////////////////////////////////////////////////////////// // class QUnixSocket /////////////////////////////////////////////////////////////////////////////// #define QUNIXSOCKET_DEFAULT_READBUFFER 1024 #define QUNIXSOCKET_DEFAULT_ANCILLARYBUFFER 0 /*! \class QUnixSocket \internal \brief The QUnixSocket class provides a Unix domain socket. \omit \ingroup Platform::DeviceSpecific \ingroup Platform::OS \ingroup Platform::Communications \endomit \ingroup qws Unix domain sockets provide an efficient mechanism for communications between Unix processes on the same machine. Unix domain sockets support a reliable, stream-oriented, connection-oriented transport protocol, much like TCP sockets. Unlike IP based sockets, the connection endpoint of a Unix domain socket is a file on disk of type socket. In addition to transporting raw data bytes, Unix domain sockets are able to transmit special ancillary data. The two types of ancillary data supported by the QUnixSocket class are: \list \o Credential Data - Allows a receiver to reliably identify the process sending each message. \o \l {QUnixSocketRights}{Rights Data } - Allows Unix file descriptors to be transmitted between processes. \endlist Because of the need to support ancillary data, QUnixSocket is not a QIODevice, like QTcpSocket and QUdpSocket. Instead, QUnixSocket contains a number of read and write methods that clients must invoke directly. Rather than returning raw data bytes, \l QUnixSocket::read() returns \l QUnixSocketMessage instances that encapsulate the message's byte data and any other ancillary data. Ancillary data is transmitted "out of band". Every \l QUnixSocketMessage received will have credential data associated with it that the client can access through calls to \l QUnixSocketMessage::processId(), \l QUnixSocketMessage::groupId() and \l QUnixSocketMessage::userId(). Likewise, message creators can set the credential data to send through calls to \l QUnixSocketMessage::setProcessId(), \l QUnixSocketMessage::setGroupId() and \l QUnixSocketMessage::setUserId() respectively. The authenticity of the credential values is verified by the system kernel and cannot be fabricated by unprivileged processes. Only processes running as the root user can specify credential data that does not match the sending process. Unix file descriptors, known as "rights data", transmitted between processes appear as though they had been dup(2)'d between the two. As Unix domain sockets present a continuous stream of bytes to the receiver, the rights data - which is transmitted out of band - must be "slotted" in at some point. The rights data is logically associated with the first byte - called the anchor byte - of the \l QUnixSocketMessage to which they are attached. Received rights data will be available from the \l QUnixSocketMessage::rights() method for the \l QUnixSocketMessage instance that contains the anchor byte. In addition to a \l QUnixSocket::write() that takes a \l QUnixSocketMessage instance - allowing a client to transmit both byte and rights data - a number of convenience overloads are provided for use when only transmitting simple byte data. Unix requires that at least one byte of raw data be transmitted in order to send rights data. A \l QUnixSocketMessage instance with rights data, but no byte data, cannot be transmitted. Unix sockets present a stream interface, such that, for example, a single six byte transmission might be received as two three byte messages. Rights data, on the other hand, is conceptually transmitted as unfragmentable datagrams. If the receiving buffer is not large enough to contain all the transmitted rights information, the data is truncated and irretreivably lost. Users should use the \l QUnixSocket::setRightsBufferSize() method to control the buffer size used for this data, and develop protocols that avoid the problem. If the buffer size is too small and rights data is truncated, the \l QUnixSocketMessage::rightsWereTruncated() flag will be set. \sa QUnixSocketMessage QUnixSocketRights */ /*! \enum QUnixSocket::SocketError The SocketError enumeration represents the various errors that can occur on a Unix domain socket. The most recent error for the socket is available through the \l QUnixSocket::error() method. \value NoError No error has occurred. \value InvalidPath An invalid path endpoint was passed to \l QUnixSocket::connect(). 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 NonexistentPath The endpoing passed to \l QUnixSocket::connect() does not refer to a Unix domain socket entity on disk. \value ConnectionRefused The connection to the specified endpoint was refused. Generally this means that there is no server listening on that endpoint. \value UnknownError An unknown error has occurred. \value ReadFailure An error occurred while reading bytes from the connection. \value WriteFailure An error occurred while writing bytes into the connection. */ /*! \enum QUnixSocket::SocketState The SocketState enumeration represents the connection state of a QUnixSocket instance. \value UnconnectedState The connection is not established. \value ConnectedState The connection is established. \value ClosingState The connection is being closed, following a call to \l QUnixSocket::close(). While closing, any pending data will be transmitted, but further writes by the application will be refused. */ /* \fn QUnixSocket::bytesWritten(qint64 bytes) This signal is emitted every time a payload of data has been written to the connection. The \a bytes argument is set to the number of bytes that were written in this payload. \sa QUnixSocket::readyRead() */ /* \fn QUnixSocket::readyRead() This signal is emitted once every time new data is available for reading from the connection. It will only be emitted again once new data is available. \sa QUnixSocket::bytesWritten() */ /*! \fn QUnixSocket::stateChanged(SocketState socketState) This signal is emitted each time the socket changes connection state. \a socketState will be set to the socket's new state. */ class QUnixSocketPrivate : public QObject { Q_OBJECT public: QUnixSocketPrivate(QUnixSocket * _me) : me(_me), fd(-1), readNotifier(0), writeNotifier(0), state(QUnixSocket::UnconnectedState), error(QUnixSocket::NoError), writeQueueBytes(0), messageValid(false), dataBuffer(0), dataBufferLength(0), dataBufferCapacity(0), ancillaryBuffer(0), ancillaryBufferCount(0), closingTimer(0) { QObject::connect(this, SIGNAL(readyRead()), me, SIGNAL(readyRead())); QObject::connect(this, SIGNAL(bytesWritten(qint64)), me, SIGNAL(bytesWritten(qint64))); } ~QUnixSocketPrivate() { if(dataBuffer) delete [] dataBuffer; if(ancillaryBuffer) delete [] ancillaryBuffer; } enum { CausedAbort = 0x70000000 }; QUnixSocket * me; int fd; QSocketNotifier * readNotifier; QSocketNotifier * writeNotifier; QUnixSocket::SocketState state; QUnixSocket::SocketError error; QQueue writeQueue; unsigned int writeQueueBytes; bool messageValid; ::msghdr message; inline void flushAncillary() { if(!messageValid) return; ::cmsghdr * h = (::cmsghdr *)CMSG_FIRSTHDR(&(message)); while(h) { if(SCM_RIGHTS == h->cmsg_type) { int * fds = (int *)CMSG_DATA(h); int numFds = (h->cmsg_len - CMSG_LEN(0)) / sizeof(int); for(int ii = 0; ii < numFds; ++ii) QT_CLOSE(fds[ii]); } h = (::cmsghdr *)CMSG_NXTHDR(&(message), h); } messageValid = false; } char * dataBuffer; unsigned int dataBufferLength; unsigned int dataBufferCapacity; char * ancillaryBuffer; inline unsigned int ancillaryBufferCapacity() { return CMSG_SPACE(sizeof(::ucred)) + CMSG_SPACE(sizeof(int) * ancillaryBufferCount); } unsigned int ancillaryBufferCount; QByteArray address; int closingTimer; virtual void timerEvent(QTimerEvent *) { me->abort(); killTimer(closingTimer); closingTimer = 0; } signals: void readyRead(); void bytesWritten(qint64); public slots: void readActivated(); qint64 writeActivated(); }; /*! Construct a QUnixSocket instance, with \a parent. The read buffer is initially set to 1024 bytes, and the rights buffer to 0 entries. \sa QUnixSocket::readBufferSize() QUnixSocket::rightsBufferSize() */ QUnixSocket::QUnixSocket(QObject * parent) : QIODevice(parent), d(new QUnixSocketPrivate(this)) { setOpenMode(QIODevice::NotOpen); setReadBufferSize(QUNIXSOCKET_DEFAULT_READBUFFER); setRightsBufferSize(QUNIXSOCKET_DEFAULT_ANCILLARYBUFFER); } /*! Construct a QUnixSocket instance, with \a parent. The read buffer is initially set to \a readBufferSize bytes, and the rights buffer to \a rightsBufferSize entries. \sa QUnixSocket::readBufferSize() QUnixSocket::rightsBufferSize() */ QUnixSocket::QUnixSocket(qint64 readBufferSize, qint64 rightsBufferSize, QObject * parent) : QIODevice(parent), d(new QUnixSocketPrivate(this)) { Q_ASSERT(readBufferSize > 0 && rightsBufferSize >= 0); setOpenMode(QIODevice::NotOpen); setReadBufferSize(readBufferSize); setRightsBufferSize(rightsBufferSize); } /*! Destroys the QUnixSocket instance. Any unsent data is discarded. */ QUnixSocket::~QUnixSocket() { abort(); delete d; } /*! Attempt to connect to \a path. This method is synchronous and will return true if the connection succeeds and false otherwise. In the case of failure, \l QUnixSocket::error() will be set accordingly. Any existing connection will be aborted, and all pending data will be discarded. \sa QUnixSocket::close() QUnixSocket::abort() QUnixSocket::error() */ bool QUnixSocket::connect(const QByteArray & path) { int _true; int crv; #ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Connect requested to '" << path << '\''; #endif abort(); // Reset any existing connection if(UnconnectedState != d->state) // abort() caused a signal and someone messed // with us. We'll assume they know what // they're doing and bail. Alternative is to // have a special "Connecting" state return false; if(path.isEmpty() || path.size() > UNIX_PATH_MAX) { d->error = InvalidPath; return false; } // Create the socket d->fd = ::socket(PF_UNIX, SOCK_STREAM, 0); if(-1 == d->fd) { #ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Unable to create socket (" << strerror(errno) << ')'; #endif d->error = ResourceError; goto connect_error; } // Set socket options _true = 1; crv = ::setsockopt(d->fd, SOL_SOCKET, SO_PASSCRED, (void *)&_true, sizeof(int)); if(-1 == crv) { #ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Unable to configure socket (" << ::strerror(errno) << ')'; #endif d->error = ResourceError; goto connect_error; } // 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 the connect crv = ::connect(d->fd, (sockaddr *)&addr, sizeof(sockaddr_un)); if(-1 == crv) { #ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Unable to connect (" << ::strerror(errno) << ')'; #endif if(ECONNREFUSED == errno) d->error = ConnectionRefused; else if(ENOENT == errno) d->error = NonexistentPath; else d->error = UnknownError; goto connect_error; } // We're connected! d->address = path; d->state = ConnectedState; d->readNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, d); d->writeNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Write, d); QObject::connect(d->readNotifier, SIGNAL(activated(int)), d, SLOT(readActivated())); QObject::connect(d->writeNotifier, SIGNAL(activated(int)), d, SLOT(writeActivated())); d->readNotifier->setEnabled(true); d->writeNotifier->setEnabled(false); setOpenMode(QIODevice::ReadWrite); emit stateChanged(ConnectedState); #ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Connected to " << path; #endif return true; connect_error: // Cleanup failed connection if(-1 != d->fd) { #ifdef QUNIXSOCKET_DEBUG int closerv = #endif QT_CLOSE(d->fd); #ifdef QUNIXSOCKET_DEBUG if(0 != closerv) { qDebug() << "QUnixSocket: Unable to close file descriptor after " "failed connect (" << ::strerror(errno) << ')'; } #endif } d->fd = -1; return false; } /*! Sets the socket descriptor to use to \a socketDescriptor, bypassing QUnixSocket's connection infrastructure, and return true on success and false on failure. \a socketDescriptor must be in the connected state, and must be a Unix domain socket descriptor. Following a successful call to this method, the QUnixSocket instance will be in the Connected state and will have assumed ownership of \a socketDescriptor. Any existing connection will be aborted, and all pending data will be discarded. \sa QUnixSocket::connect() */ bool QUnixSocket::setSocketDescriptor(int socketDescriptor) { abort(); if(UnconnectedState != state()) // See QUnixSocket::connect() return false; // Attempt to set the socket options if(-1 == socketDescriptor) { #ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: User provided socket is invalid"; #endif d->error = ResourceError; return false; } // Set socket options int _true = 1; int crv = ::setsockopt(socketDescriptor, SOL_SOCKET, SO_PASSCRED, (void *)&_true, sizeof(int)); if(-1 == crv) { #ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Unable to configure client provided socket (" << ::strerror(errno) << ')'; #endif d->error = ResourceError; return false; } d->fd = socketDescriptor; d->state = ConnectedState; d->address = QByteArray(); setOpenMode(QIODevice::ReadWrite); d->readNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, d); d->writeNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Write, d); QObject::connect(d->readNotifier, SIGNAL(activated(int)), d, SLOT(readActivated())); QObject::connect(d->writeNotifier, SIGNAL(activated(int)), d, SLOT(writeActivated())); d->readNotifier->setEnabled(true); d->writeNotifier->setEnabled(false); emit stateChanged(d->state); return true; } /*! Returns the socket descriptor currently in use. This method will return -1 if the QUnixSocket instance is in the UnconnectedState \l {QUnixSocket::state()}{state. } \sa QUnixSocket::setSocketDescriptor() */ int QUnixSocket::socketDescriptor() const { return d->fd; } /*! Abort the connection. This will immediately disconnect (if connected) and discard any pending data. Following a call to QUnixSocket::abort() the object will always be in the disconnected \link QUnixSocket::state() state. \endlink \sa QUnixSocket::close() */ void QUnixSocket::abort() { setOpenMode(QIODevice::NotOpen); // We want to be able to use QUnixSocket::abort() to cleanup our state but // also preserve the error message that caused the abort. It is not // possible to reorder code to do this: // abort(); // d->error = SomeError // as QUnixSocket::abort() might emit a signal and we need the error to be // set within that signal. So, if we want an error message to be preserved // across a *single* call to abort(), we set the // QUnixSocketPrivate::CausedAbort flag in the error. if(d->error & QUnixSocketPrivate::CausedAbort) d->error = (QUnixSocket::SocketError)(d->error & ~QUnixSocketPrivate::CausedAbort); else d->error = NoError; if( UnconnectedState == d->state) return; #ifdef QUNIXSOCKET_DEBUG int closerv = #endif ::close(d->fd); #ifdef QUNIXSOCKET_DEBUG if(0 != closerv) { qDebug() << "QUnixSocket: Unable to close socket during abort (" << strerror(errno) << ')'; } #endif // Reset variables d->fd = -1; d->state = UnconnectedState; d->dataBufferLength = 0; d->flushAncillary(); d->address = QByteArray(); if(d->readNotifier) { d->readNotifier->setEnabled(false); d->readNotifier->deleteLater(); } if(d->writeNotifier) { d->writeNotifier->setEnabled(false); d->writeNotifier->deleteLater(); } d->readNotifier = 0; d->writeNotifier = 0; d->writeQueue.clear(); d->writeQueueBytes = 0; if(d->closingTimer) { d->killTimer(d->closingTimer); } d->closingTimer = 0; emit stateChanged(d->state); } /*! Close the connection. The instance will enter the Closing \l {QUnixSocket::state()}{state } until all pending data has been transmitted, at which point it will enter the Unconnected state. Even if there is no pending data for transmission, the object will never jump directly to Disconnect without first passing through the Closing state. \sa QUnixSocket::abort() */ void QUnixSocket::close() { if(ConnectedState != state()) return; d->state = ClosingState; if(d->writeQueue.isEmpty()) { d->closingTimer = d->startTimer(0); // Start a timer to "fake" // completing writes } emit stateChanged(d->state); } /*! This function writes as much as possible from the internal write buffer to the underlying socket, without blocking. If any data was written, this function returns true; otherwise false is returned. */ // Note! docs partially copied from QAbstractSocket::flush() bool QUnixSocket::flush() { // This needs to have the same semantics as QAbstractSocket, if it is to // be used interchangeably with that class. if (d->writeQueue.isEmpty()) return false; d->writeActivated(); return true; } /*! Returns the last error to have occurred on this object. This method is not destructive, so multiple calls to QUnixSocket::error() will return the same value. The error is only reset by a call to \l QUnixSocket::connect() or \l QUnixSocket::abort() */ QUnixSocket::SocketError QUnixSocket::error() const { return (QUnixSocket::SocketError) (d->error & ~QUnixSocketPrivate::CausedAbort); } /*! Returns the connection state of this instance. */ QUnixSocket::SocketState QUnixSocket::state() const { return d->state; } /*! Returns the Unix path address passed to \l QUnixSocket::connect(). This method will return an empty path if the object is in the Unconnected \l {QUnixSocket::state()}{state } or was connected through a call to \l QUnixSocket::setSocketDescriptor() \sa QUnixSocket::connect() QUnixSocket::setSocketDescriptor() */ QByteArray QUnixSocket::address() const { return d->address; } /*! Returns the number of bytes available for immediate retrieval through a call to \l QUnixSocket::read(). */ qint64 QUnixSocket::bytesAvailable() const { return QIODevice::bytesAvailable() + d->dataBufferLength; } /*! Returns the number of enqueued bytes still to be written to the socket. */ qint64 QUnixSocket::bytesToWrite() const { return d->writeQueueBytes; } /*! Returns the size of the read buffer in bytes. The read buffer size determines the amount of byte data that can be read from the socket in one go. The read buffer size caps the maximum value that can be returned by \l QUnixSocket::bytesAvailable() and will always be greater than zero. By default, the read buffer size is 1024 bytes. The size of the read buffer is independent of the rights buffer, which can be queried by \l QUnixSocket::rightsBufferSize(). \sa QUnixSocket::setReadBufferSize() */ qint64 QUnixSocket::readBufferSize() const { return d->dataBufferCapacity; } /*! Sets the \a size of the socket's read buffer in bytes. The size of the read buffer is independent of the rights buffer, which can be set by \l QUnixSocket::setRightsBufferSize(). Attempting to reduce the buffer size while bytes are available for reading (ie. while the buffer is in use) will fail. \sa QUnixSocket::readBufferSize() */ void QUnixSocket::setReadBufferSize(qint64 size) { Q_ASSERT(size > 0); if(size == d->dataBufferCapacity || d->dataBufferLength) return; if(d->dataBuffer) delete [] d->dataBuffer; d->dataBuffer = new char[size]; d->dataBufferCapacity = size; } /*! Returns the size of the rights buffer in rights entries. The rights buffer size determines the number of rights transferences that can be received in any message. Unlike byte stream data which can be fragmented into many smaller messages if the \link QUnixSocket::readBufferSize() read buffer \endlink is not large enough to contain all the available data, rights data is transmitted as unfragmentable datagrams. If the rights buffer is not large enough to contain this unfragmentable datagram, the datagram will be truncated and rights data irretrievably lost. If truncation occurs, the \l QUnixSocketMessage::rightsWereTruncated() flag will be set. By default the rights buffer size is 0 entries - rights data cannot be received. The size of the rights buffer is independent of the read buffer, which can be queried by \l QUnixSocket::readBufferSize(). \sa QUnixSocket::setRightsBufferSize() */ qint64 QUnixSocket::rightsBufferSize() const { return d->ancillaryBufferCount; } /*! Sets the \a size of the socket's rights buffer in rights entries. The size of the rights buffer is independent of the read buffer, which can be set by \l QUnixSocket::setReadBufferSize(). Attempting to reduce the buffer size while bytes are available for reading (ie. while the buffer is in use) will fail. \sa QUnixSocket::rightsBufferSize() */ void QUnixSocket::setRightsBufferSize(qint64 size) { Q_ASSERT(size >= 0); if((size == d->ancillaryBufferCount || d->dataBufferLength) && d->ancillaryBuffer) return; qint64 byteSize = CMSG_SPACE(sizeof(::ucred)) + CMSG_SPACE(size * sizeof(int)); if(d->ancillaryBuffer) delete [] d->ancillaryBuffer; d->ancillaryBuffer = new char[byteSize]; d->ancillaryBufferCount = size; } /*! \overload Writes \a socketdata to the socket. In addition to failing if the socket is not in the Connected state, writing will fail if \a socketdata is \l {QUnixSocketMessage::isValid()}{invalid. } Writes through the QUnixSocket class are asynchronous. Rather than being written immediately, data is enqueued and written once the application reenters the Qt event loop and the socket becomes available for writing. Thus, this method will only fail if the socket is not in the Connected state - it is illegal to attempt a write on a Unconnected or Closing socket. Applications can monitor the progress of data writes through the \l QUnixSocket::bytesWritten() signal and \l QUnixSocket::bytesToWrite() method. \sa QUnixSocketMessage */ qint64 QUnixSocket::write(const QUnixSocketMessage & socketdata) { if(ConnectedState != state() || !socketdata.isValid()) return -1; if(socketdata.d->size() == 0) return 0; d->writeQueue.enqueue(socketdata); d->writeQueueBytes += socketdata.d->size(); d->writeNotifier->setEnabled(true); return socketdata.d->size(); } /*! Return the next available message, or an empty message if none is available. To avoid retrieving empty messages, applications should connect to the \l QUnixSocket::readyRead() signal to be notified when new messages are available or periodically poll the \l QUnixSocket::bytesAvailable() method. \sa QUnixSocket::readyRead() QUnixSocket::bytesAvailable() */ QUnixSocketMessage QUnixSocket::read() { QUnixSocketMessage data; if(!d->dataBufferLength) return data; data.d->state = QUnixSocketMessagePrivate::Credential; // Bytes are easy data.setBytes(QByteArray(d->dataBuffer, d->dataBufferLength)); // Extract ancillary data QList a; ::cmsghdr * h = (::cmsghdr *)CMSG_FIRSTHDR(&(d->message)); while(h) { if(SCM_CREDENTIALS == h->cmsg_type) { ::ucred * cred = (::ucred *)CMSG_DATA(h); #ifdef QUNIXSOCKET_DEBUG qDebug( "Credentials recd: pid %lu - gid %lu - uid %lu", cred->pid, cred->gid, cred->uid ); #endif data.d->pid = cred->pid; data.d->gid = cred->gid; data.d->uid = cred->uid; } else if(SCM_RIGHTS == h->cmsg_type) { int * fds = (int *)CMSG_DATA(h); int numFds = (h->cmsg_len - CMSG_LEN(0)) / sizeof(int); for(int ii = 0; ii < numFds; ++ii) { QUnixSocketRights qusr(fds[ii], 0); a.append(qusr); } } else { #ifdef QUNIXSOCKET_DEBUG qFatal("QUnixSocket: Unknown ancillary data type (%d) received.", h->cmsg_type); #endif } h = (::cmsghdr *)CMSG_NXTHDR(&(d->message), h); } if(d->message.msg_flags & MSG_CTRUNC) { data.d->state = (QUnixSocketMessagePrivate::AncillaryDataState)(QUnixSocketMessagePrivate::Truncated | QUnixSocketMessagePrivate::Credential ); } if(!a.isEmpty()) data.d->rights = a; d->dataBufferLength = 0; d->messageValid = false; d->readNotifier->setEnabled(true); return data; } /*! \internal */ bool QUnixSocket::isSequential() const { return true; } /*! \internal */ bool QUnixSocket::waitForReadyRead(int msecs) { if(UnconnectedState == d->state) return false; if(d->messageValid) { return true; } Q_ASSERT(-1 != d->fd); int timeout = msecs; struct timeval tv; struct timeval *ptrTv = 0; QTime stopWatch; stopWatch.start(); do { fd_set readset; FD_ZERO(&readset); FD_SET(d->fd, &readset); if(-1 != msecs) { tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; ptrTv = &tv; } int rv = ::select(d->fd + 1, &readset, 0, 0, ptrTv); switch(rv) { case 0: // timeout return false; case 1: // ok d->readActivated(); return true; default: if (errno != EINTR) abort(); // error break; } timeout = msecs - stopWatch.elapsed(); } while (timeout > 0); return false; } bool QUnixSocket::waitForBytesWritten(int msecs) { if(UnconnectedState == d->state) return false; Q_ASSERT(-1 != d->fd); if ( d->writeQueue.isEmpty() ) return true; QTime stopWatch; stopWatch.start(); while ( true ) { fd_set fdwrite; FD_ZERO(&fdwrite); FD_SET(d->fd, &fdwrite); int timeout = msecs < 0 ? 0 : msecs - stopWatch.elapsed(); struct timeval tv; struct timeval *ptrTv = 0; if ( -1 != msecs ) { tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; ptrTv = &tv; } int rv = ::select(d->fd + 1, 0, &fdwrite, 0, ptrTv); switch ( rv ) { case 0: // timeout return false; case 1: { // ok to write qint64 bytesWritten = d->writeActivated(); if (bytesWritten == 0) { // We need to retry int delay = 1; do { if (-1 != msecs) { timeout = msecs - stopWatch.elapsed(); if (timeout <= 0) { // We have exceeded our allotted time return false; } else { if (delay > timeout) delay = timeout; } } // Pause before we make another attempt to send ::usleep(delay * 1000); if (delay < 1024) delay *= 2; bytesWritten = d->writeActivated(); } while (bytesWritten == 0); } return (bytesWritten != -1); } default: // error - or an uncaught signal!!!!!!!!! if ( rv == EINTR ) continue; abort(); return false; } } return false; // fix warnings } /*! \internal */ bool QUnixSocket::canReadLine() const { for(unsigned int ii = 0; ii < d->dataBufferLength; ++ii) if(d->dataBuffer[ii] == '\n') return true; return false; } /*! \internal */ qint64 QUnixSocket::readData(char * data, qint64 maxSize) { Q_ASSERT(data); if(0 >= maxSize) return 0; if(!d->dataBufferLength) return 0; // Read data unsigned int size = d->dataBufferLength>maxSize?maxSize:d->dataBufferLength; memcpy(data, d->dataBuffer, size); if(size == d->dataBufferLength) { d->dataBufferLength = 0; } else { memmove(d->dataBuffer, d->dataBuffer + size, d->dataBufferLength - size); d->dataBufferLength -= size; } // Flush ancillary d->flushAncillary(); if(0 == d->dataBufferLength) d->readNotifier->setEnabled(true); return size; } /*! \internal */ qint64 QUnixSocket::writeData (const char * data, qint64 maxSize) { return write(QUnixSocketMessage(QByteArray(data, maxSize))); } qint64 QUnixSocketPrivate::writeActivated() { writeNotifier->setEnabled(false); QUnixSocketMessage & m = writeQueue.head(); const QList & a = m.rights(); // // Construct the message // ::iovec vec; if ( !m.d->vec ) // message does not already have an iovec { vec.iov_base = (void *)m.bytes().constData(); vec.iov_len = m.bytes().size(); } // Allocate the control buffer ::msghdr sendmessage; ::bzero(&sendmessage, sizeof(::msghdr)); if ( m.d->vec ) { sendmessage.msg_iov = m.d->vec; sendmessage.msg_iovlen = m.d->iovecLen; } else { sendmessage.msg_iov = &vec; sendmessage.msg_iovlen = 1; } unsigned int required = CMSG_SPACE(sizeof(::ucred)) + a.size() * CMSG_SPACE(sizeof(int)); sendmessage.msg_control = new char[required]; ::bzero(sendmessage.msg_control, required); sendmessage.msg_controllen = required; // Create ancillary buffer ::cmsghdr * h = CMSG_FIRSTHDR(&sendmessage); if(m.d->state & QUnixSocketMessagePrivate::Credential) { h->cmsg_len = CMSG_LEN(sizeof(::ucred)); h->cmsg_level = SOL_SOCKET; h->cmsg_type = SCM_CREDENTIALS; ((::ucred *)CMSG_DATA(h))->pid = m.d->pid; ((::ucred *)CMSG_DATA(h))->gid = m.d->gid; ((::ucred *)CMSG_DATA(h))->uid = m.d->uid; h = CMSG_NXTHDR(&sendmessage, h); } else { sendmessage.msg_controllen -= CMSG_SPACE(sizeof(::ucred)); } for(int ii = 0; ii < a.count(); ++ii) { const QUnixSocketRights & r = a.at(ii); if(r.isValid()) { h->cmsg_len = CMSG_LEN(sizeof(int)); h->cmsg_level = SOL_SOCKET; h->cmsg_type = SCM_RIGHTS; *((int *)CMSG_DATA(h)) = r.peekFd(); h = CMSG_NXTHDR(&sendmessage, h); } else { sendmessage.msg_controllen -= CMSG_SPACE(sizeof(int)); } } #ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Transmitting message (length" << m.d->size() << ')'; #endif ::ssize_t s = ::sendmsg(fd, &sendmessage, MSG_DONTWAIT | MSG_NOSIGNAL); #ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Transmitted message (" << s << ')'; #endif if(-1 == s) { if(EAGAIN == errno || EWOULDBLOCK == errno || EINTR == errno) { writeNotifier->setEnabled(true); } else if(EPIPE == errno) { #ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Remote side disconnected during transmit " "(" << ::strerror(errno) << ')'; #endif me->abort(); } else { #ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Unable to transmit data (" << ::strerror(errno) << ')'; #endif error = (QUnixSocket::SocketError)(QUnixSocket::WriteFailure | CausedAbort); me->abort(); } } else if(s != m.d->size()) { // A partial transmission writeNotifier->setEnabled(true); delete [] (char *)sendmessage.msg_control; m.d->rights = QList(); m.d->removeBytes( s ); writeQueueBytes -= s; emit bytesWritten(s); return s; } else { // Success! writeQueue.dequeue(); Q_ASSERT(writeQueueBytes >= (unsigned)s); writeQueueBytes -= s; emit bytesWritten(s); } delete [] (char *)sendmessage.msg_control; if(-1 != s && !writeQueue.isEmpty()) return writeActivated(); else if(QUnixSocket::ClosingState == me->state() && writeQueue.isEmpty()) me->abort(); if((-1 == s) && (EAGAIN == errno || EWOULDBLOCK == errno || EINTR == errno)) // Return zero bytes written to indicate retry may be required return 0; else return s; } void QUnixSocketPrivate::readActivated() { #ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: readActivated"; #endif readNotifier->setEnabled(false); ::iovec vec; vec.iov_base = dataBuffer; vec.iov_len = dataBufferCapacity; bzero(&message, sizeof(::msghdr)); message.msg_iov = &vec; message.msg_iovlen = 1; message.msg_controllen = ancillaryBufferCapacity(); message.msg_control = ancillaryBuffer; int flags = 0; #ifdef MSG_CMSG_CLOEXEC flags = MSG_CMSG_CLOEXEC; #endif int recvrv = ::recvmsg(fd, &message, flags); #ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Received message (" << recvrv << ')'; #endif if(-1 == recvrv) { #ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Unable to receive data (" << ::strerror(errno) << ')'; #endif error = (QUnixSocket::SocketError)(QUnixSocket::ReadFailure | CausedAbort); me->abort(); } else if(0 == recvrv) { me->abort(); } else { Q_ASSERT(recvrv); Q_ASSERT((unsigned)recvrv <= dataBufferCapacity); dataBufferLength = recvrv; messageValid = true; #ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: readyRead() " << dataBufferLength; #endif emit readyRead(); } } QT_END_NAMESPACE #include "qunixsocket.moc"