1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtDBus module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40
41 #include "qdbusunixfiledescriptor.h"
42
43 #ifdef Q_OS_UNIX
44 # include <private/qcore_unix_p.h>
45 #endif
46
47 QT_BEGIN_NAMESPACE
48
49 #ifndef QT_NO_DBUS
50
51 /*!
52 \class QDBusUnixFileDescriptor
53 \inmodule QtDBus
54 \ingroup shared
55 \since 4.8
56
57 \brief The QDBusUnixFileDescriptor class holds one Unix file descriptor.
58
59 The QDBusUnixFileDescriptor class is used to hold one Unix file
60 descriptor for use with the Qt D-Bus module. This allows applications to
61 send and receive Unix file descriptors over the D-Bus connection, mapping
62 automatically to the D-Bus type 'h'.
63
64 Objects of type QDBusUnixFileDescriptors can be used also as parameters
65 in signals and slots that get exported to D-Bus by registering with
66 QDBusConnection::registerObject.
67
68 QDBusUnixFileDescriptor does not take ownership of the file descriptor.
69 Instead, it will use the Unix system call \c dup(2) to make a copy of the
70 file descriptor. This file descriptor belongs to the
71 QDBusUnixFileDescriptor object and should not be stored or closed by the
72 user. Instead, you should make your own copy if you need that.
73
74 \section2 Availability
75
76 Unix file descriptor passing is not available in all D-Bus connections.
77 This feature is present with D-Bus library and bus daemon version 1.4 and
78 upwards on Unix systems. Qt D-Bus automatically enables the feature if such
79 a version was found at compile-time and run-time.
80
81 To verify that your connection does support passing file descriptors,
82 check if the QDBusConnection::UnixFileDescriptorPassing capability is set
83 with QDBusConnection::connectionCapabilities(). If the flag is not
84 active, then you will not be able to make calls to methods that have
85 QDBusUnixFileDescriptor as arguments or even embed such a type in a
86 variant. You will also not receive calls containing that type.
87
88 Note also that remote applications may not have support for Unix file
89 descriptor passing. If you make a D-Bus to a remote application that
90 cannot receive such a type, you will receive an error reply. If you try
91 to send a signal containing a D-Bus file descriptor or return one from a
92 method call, the message will be silently dropped.
93
94 Even if the feature is not available, QDBusUnixFileDescriptor will
95 continue to operate, so code need not have compile-time checks for the
96 availability of this feature.
97
98 On non-Unix systems, QDBusUnixFileDescriptor will always report an
99 invalid state and QDBusUnixFileDescriptor::isSupported() will return
100 false.
101
102 \sa QDBusConnection::ConnectionCapabilities, QDBusConnection::connectionCapabilities()
103 */
104
105 /*!
106 \typedef QDBusUnixFileDescriptor::Data
107 \internal
108 */
109
110 /*!
111 \variable QDBusUnixFileDescriptor::d
112 \internal
113 */
114
115 class QDBusUnixFileDescriptorPrivate : public QSharedData {
116 public:
QDBusUnixFileDescriptorPrivate()117 QDBusUnixFileDescriptorPrivate() : fd(-1) { }
QDBusUnixFileDescriptorPrivate(const QDBusUnixFileDescriptorPrivate & other)118 QDBusUnixFileDescriptorPrivate(const QDBusUnixFileDescriptorPrivate &other)
119 : QSharedData(other), fd(-1)
120 { }
121 ~QDBusUnixFileDescriptorPrivate();
122
123 QAtomicInt fd;
124 };
125
126 template<> inline
~QExplicitlySharedDataPointer()127 QExplicitlySharedDataPointer<QDBusUnixFileDescriptorPrivate>::~QExplicitlySharedDataPointer()
128 { if (d && !d->ref.deref()) delete d; }
129
130 /*!
131 Constructs a QDBusUnixFileDescriptor without a wrapped file descriptor.
132 This is equivalent to constructing the object with an invalid file
133 descriptor (like -1).
134
135 \sa fileDescriptor(), isValid()
136 */
QDBusUnixFileDescriptor()137 QDBusUnixFileDescriptor::QDBusUnixFileDescriptor()
138 : d(nullptr)
139 {
140 }
141
142 /*!
143 Constructs a QDBusUnixFileDescriptor object by copying the \a
144 fileDescriptor parameter. The original file descriptor is not touched and
145 must be closed by the user.
146
147 Note that the value returned by fileDescriptor() will be different from
148 the \a fileDescriptor parameter passed.
149
150 If the \a fileDescriptor parameter is not valid, isValid() will return
151 false and fileDescriptor() will return -1.
152
153 \sa setFileDescriptor(), fileDescriptor()
154 */
QDBusUnixFileDescriptor(int fileDescriptor)155 QDBusUnixFileDescriptor::QDBusUnixFileDescriptor(int fileDescriptor)
156 : d(nullptr)
157 {
158 if (fileDescriptor != -1)
159 setFileDescriptor(fileDescriptor);
160 }
161
162 /*!
163 Constructs a QDBusUnixFileDescriptor object by copying \a other.
164 */
QDBusUnixFileDescriptor(const QDBusUnixFileDescriptor & other)165 QDBusUnixFileDescriptor::QDBusUnixFileDescriptor(const QDBusUnixFileDescriptor &other)
166 : d(other.d)
167 {
168 }
169
170 /*!
171 Copies the Unix file descriptor from the \a other QDBusUnixFileDescriptor
172 object. If the current object contained a file descriptor, it will be
173 properly disposed of before.
174 */
operator =(const QDBusUnixFileDescriptor & other)175 QDBusUnixFileDescriptor &QDBusUnixFileDescriptor::operator=(const QDBusUnixFileDescriptor &other)
176 {
177 if (this != &other)
178 d.operator=(other.d);
179 return *this;
180 }
181
182 /*!
183 \fn QDBusUnixFileDescriptor &QDBusUnixFileDescriptor::operator=(QDBusUnixFileDescriptor &&other)
184
185 Move-assigns \a other to this QDBusUnixFileDescriptor.
186 */
187
188 /*!
189 Destroys this QDBusUnixFileDescriptor object and disposes of the Unix file descriptor that it contained.
190 */
~QDBusUnixFileDescriptor()191 QDBusUnixFileDescriptor::~QDBusUnixFileDescriptor()
192 {
193 }
194
195 /*!
196 \fn void QDBusUnixFileDescriptor::swap(QDBusUnixFileDescriptor &other)
197 \since 5.0
198
199 Swaps this file descriptor instance with \a other. This function
200 is very fast and never fails.
201 */
202
203 /*!
204 Returns \c true if this Unix file descriptor is valid. A valid Unix file
205 descriptor is not -1.
206
207 \sa fileDescriptor()
208 */
isValid() const209 bool QDBusUnixFileDescriptor::isValid() const
210 {
211 return d ? d->fd.loadRelaxed() != -1 : false;
212 }
213
214 /*!
215 Returns the Unix file descriptor contained by this
216 QDBusUnixFileDescriptor object. An invalid file descriptor is represented
217 by the value -1.
218
219 Note that the file descriptor returned by this function is owned by the
220 QDBusUnixFileDescriptor object and must not be stored past the lifetime
221 of this object. It is ok to use it while this object is valid, but if one
222 wants to store it for longer use, the file descriptor should be cloned
223 using the Unix \c dup(2), \c dup2(2) or \c dup3(2) functions.
224
225 \sa isValid()
226 */
fileDescriptor() const227 int QDBusUnixFileDescriptor::fileDescriptor() const
228 {
229 return d ? d->fd.loadRelaxed() : -1;
230 }
231
232 // actual implementation
233 #ifdef Q_OS_UNIX
234
235 // qdoc documentation is generated on Unix
236
237 /*!
238 Returns \c true if Unix file descriptors are supported on this platform. In
239 other words, this function returns \c true if this is a Unix platform.
240
241 Note that QDBusUnixFileDescriptor continues to operate even if this
242 function returns \c false. The only difference is that the
243 QDBusUnixFileDescriptor objects will always be in the isValid() == false
244 state and fileDescriptor() will always return -1. The class will not
245 consume any operating system resources.
246 */
isSupported()247 bool QDBusUnixFileDescriptor::isSupported()
248 {
249 return true;
250 }
251
252 /*!
253 Sets the file descriptor that this QDBusUnixFileDescriptor object holds
254 to a copy of \a fileDescriptor. The original file descriptor is not
255 touched and must be closed by the user.
256
257 Note that the value returned by fileDescriptor() will be different from
258 the \a fileDescriptor parameter passed.
259
260 If the \a fileDescriptor parameter is not valid, isValid() will return
261 false and fileDescriptor() will return -1.
262
263 \sa isValid(), fileDescriptor()
264 */
setFileDescriptor(int fileDescriptor)265 void QDBusUnixFileDescriptor::setFileDescriptor(int fileDescriptor)
266 {
267 if (fileDescriptor != -1)
268 giveFileDescriptor(qt_safe_dup(fileDescriptor));
269 }
270
271 /*!
272 \internal
273 Sets the Unix file descriptor to \a fileDescriptor without copying.
274
275 \sa setFileDescriptor()
276 */
giveFileDescriptor(int fileDescriptor)277 void QDBusUnixFileDescriptor::giveFileDescriptor(int fileDescriptor)
278 {
279 // if we are the sole ref, d remains unchanged
280 // if detaching happens, d->fd will be -1
281 if (d)
282 d.detach();
283 else
284 d = new QDBusUnixFileDescriptorPrivate;
285
286 const int fdl = d->fd.loadRelaxed();
287 if (fdl != -1)
288 qt_safe_close(fdl);
289
290 if (fileDescriptor != -1)
291 d->fd.storeRelaxed(fileDescriptor);
292 }
293
294 /*!
295 \internal
296 Extracts the Unix file descriptor from the QDBusUnixFileDescriptor object
297 and transfers ownership.
298
299 Note: since QDBusUnixFileDescriptor is implicitly shared, this function
300 is inherently racy and should be avoided.
301 */
takeFileDescriptor()302 int QDBusUnixFileDescriptor::takeFileDescriptor()
303 {
304 if (!d)
305 return -1;
306
307 return d->fd.fetchAndStoreRelaxed(-1);
308 }
309
~QDBusUnixFileDescriptorPrivate()310 QDBusUnixFileDescriptorPrivate::~QDBusUnixFileDescriptorPrivate()
311 {
312 const int fdl = fd.loadRelaxed();
313 if (fdl != -1)
314 qt_safe_close(fdl);
315 }
316
317 #else
isSupported()318 bool QDBusUnixFileDescriptor::isSupported()
319 {
320 return false;
321 }
322
setFileDescriptor(int)323 void QDBusUnixFileDescriptor::setFileDescriptor(int)
324 {
325 }
326
giveFileDescriptor(int)327 void QDBusUnixFileDescriptor::giveFileDescriptor(int)
328 {
329 }
330
takeFileDescriptor()331 int QDBusUnixFileDescriptor::takeFileDescriptor()
332 {
333 return -1;
334 }
335
~QDBusUnixFileDescriptorPrivate()336 QDBusUnixFileDescriptorPrivate::~QDBusUnixFileDescriptorPrivate()
337 {
338 }
339
340 #endif
341
342 #endif // QT_NO_DBUS
343
344 QT_END_NAMESPACE
345