diff options
Diffstat (limited to 'src/gui/embedded/qunixsocket.cpp')
-rw-r--r-- | src/gui/embedded/qunixsocket.cpp | 1800 |
1 files changed, 0 insertions, 1800 deletions
diff --git a/src/gui/embedded/qunixsocket.cpp b/src/gui/embedded/qunixsocket.cpp deleted file mode 100644 index 85de32da4a..0000000000 --- a/src/gui/embedded/qunixsocket.cpp +++ /dev/null @@ -1,1800 +0,0 @@ -/**************************************************************************** -** -** 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 "qunixsocket_p.h" - -// #define QUNIXSOCKET_DEBUG 1 - -#include <QtCore/qsocketnotifier.h> -#include <QtCore/qqueue.h> -#include <QtCore/qdatetime.h> -#include "private/qcore_unix_p.h" // overrides QT_OPEN - -#ifdef QUNIXSOCKET_DEBUG -#include <QtCore/qdebug.h> -#endif - -extern "C" { -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <sys/socket.h> -#include <sys/un.h> -}; - -#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<QUnixSocketRights> & 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<QUnixSocketRights> 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<char**>(&(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<QUnixSocketRights> & 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<iovec*>(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<QUnixSocketRights> & rights) -{ - d.detach(); - d->rights = rights; -} - -/*! - Return the rights portion of the message. - - \sa QUnixSocketMessage::setRights() - */ -const QList<QUnixSocketRights> & 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<QUnixSocketMessage> 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<QUnixSocketRights> 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<QUnixSocketRights> & 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<QUnixSocketRights>(); - 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" |