/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "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"