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

source code of qtbase/src/dbus/qdbusunixfiledescriptor.cpp