📄 qunixsocket.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.**** This file is part of the QtGui module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.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 GNU** General Public Licensing requirements will be met:** http://trolltech.com/products/qt/licenses/licensing/opensource/**** If you are unsure which license is appropriate for your use, please** review the following information:** http://trolltech.com/products/qt/licenses/licensing/licensingoverview** or contact the sales department at sales@trolltech.com.**** In addition, as a special exception, Trolltech gives you certain** additional rights. These rights are described in the Trolltech GPL** Exception version 1.0, which can be found at** http://www.trolltech.com/products/qt/gplexception/ and in the file** GPL_EXCEPTION.txt in this package.**** In addition, as a special exception, Trolltech, as the sole copyright** holder for Qt Designer, grants users of the Qt/Eclipse Integration** plug-in the right for the Qt/Eclipse Integration to link to** functionality provided by Qt Designer and its related libraries.**** Trolltech reserves all rights not expressly granted herein.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#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>#endifextern "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_LSB// LSB doesn't declare ucredstruct 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_LSB///////////////////////////////////////////////////////////////////////////////// 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -