summaryrefslogtreecommitdiff
path: root/src/gui/embedded/qunixsocket.cpp
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 10:34:13 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 10:34:13 +0100
commit67ad0519fd165acee4a4d2a94fa502e9e4847bd0 (patch)
tree1dbf50b3dff8d5ca7e9344733968c72704eb15ff /src/gui/embedded/qunixsocket.cpp
downloadqt4-tools-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.gz
Long live Qt!
Diffstat (limited to 'src/gui/embedded/qunixsocket.cpp')
-rw-r--r--src/gui/embedded/qunixsocket.cpp1794
1 files changed, 1794 insertions, 0 deletions
diff --git a/src/gui/embedded/qunixsocket.cpp b/src/gui/embedded/qunixsocket.cpp
new file mode 100644
index 0000000000..16f2caecb8
--- /dev/null
+++ b/src/gui/embedded/qunixsocket.cpp
@@ -0,0 +1,1794 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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>
+
+#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
+ ::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 = ::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 = ::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)
+ ::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
+ ::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 recvrv = ::recvmsg(fd, &message, 0);
+#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"