1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the QtSystems module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL21$ |
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 http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://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 2.1 or version 3 as published by the Free |
20 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and |
21 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the |
22 | ** following information to ensure the GNU Lesser General Public License |
23 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and |
24 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
25 | ** |
26 | ** As a special exception, The Qt Company gives you certain additional |
27 | ** rights. These rights are described in The Qt Company LGPL Exception |
28 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
29 | ** |
30 | ** $QT_END_LICENSE$ |
31 | ** |
32 | ****************************************************************************/ |
33 | |
34 | #include "qremoteserviceregister.h" |
35 | #include "qremoteserviceregisterentry_p.h" |
36 | #include "ipc/instancemanager_p.h" |
37 | #include "qremoteserviceregister_p.h" |
38 | #include "qserviceclientcredentials_p.h" |
39 | |
40 | #include <QtCore/QDataStream> |
41 | #include <QtCore/QEvent> |
42 | |
43 | QT_BEGIN_NAMESPACE |
44 | |
45 | /*! |
46 | \class QRemoteServiceRegister::Entry |
47 | \ingroup servicefw |
48 | \inmodule QtServiceFramework |
49 | \brief The Entry class represents a remote service entry to be published on QRemoteServiceRegister. |
50 | |
51 | This class is created using QRemoteServiceRegister::createEntry to supply remote service |
52 | details matching a valid QServiceInterfaceDescriptor. |
53 | |
54 | A registration entry can then be published for discovery by remote clients. |
55 | |
56 | */ |
57 | |
58 | /*! |
59 | Constructs a null registration entry. |
60 | */ |
61 | QRemoteServiceRegister::Entry::Entry() |
62 | { |
63 | d = new QRemoteServiceRegisterEntryPrivate; |
64 | } |
65 | |
66 | /*! |
67 | Constructs the registration entry that is a copy of \a other. |
68 | */ |
69 | QRemoteServiceRegister::Entry::Entry(const Entry& other) |
70 | : d(other.d) |
71 | { |
72 | } |
73 | |
74 | /*! |
75 | Destroys the registration entry. |
76 | */ |
77 | QRemoteServiceRegister::Entry::~Entry() |
78 | { |
79 | } |
80 | |
81 | /*! |
82 | Checks if the registration entry is currently a valid remote service entry |
83 | |
84 | Returns true if the serviceName(), interfaceName() and version() point to |
85 | a valid QServiceInterfaceDescriptor, otherwise false. |
86 | */ |
87 | bool QRemoteServiceRegister::Entry::isValid() const |
88 | { |
89 | if (!d->iface.isEmpty() && !d->service.isEmpty() |
90 | && !d->ifaceVersion.isEmpty() && d->cptr!=0 && d->meta!=0) |
91 | return true; |
92 | return false; |
93 | } |
94 | |
95 | /*! |
96 | Returns true if this font is equal to \a other; otherwise false. |
97 | */ |
98 | bool QRemoteServiceRegister::Entry::operator==(const Entry& other) const |
99 | { |
100 | return d->service == other.d->service && |
101 | d->iface == other.d->iface && |
102 | d->ifaceVersion == other.d->ifaceVersion; |
103 | } |
104 | |
105 | /*! |
106 | Returns true if this font is different from \a other; otherwise false. |
107 | */ |
108 | bool QRemoteServiceRegister::Entry::operator!=(const Entry& other) const |
109 | { |
110 | return !(other == *this); |
111 | } |
112 | |
113 | /*! |
114 | Assigns \a other to this registration entry and returns a reference to it. |
115 | */ |
116 | QRemoteServiceRegister::Entry &QRemoteServiceRegister::Entry::operator=(const Entry& other) |
117 | { |
118 | d = other.d; |
119 | return *this; |
120 | } |
121 | |
122 | /*! |
123 | Returns the interface name of the registration entry. |
124 | |
125 | This should correspond to the interface name from the service XML description. |
126 | |
127 | \sa serviceName(), version() |
128 | */ |
129 | QString QRemoteServiceRegister::Entry::interfaceName() const |
130 | { |
131 | return d->iface; |
132 | } |
133 | |
134 | /*! |
135 | Returns the service name of the registration entry. |
136 | |
137 | This should correspond to the service name from the service XML description. |
138 | |
139 | \sa interfaceName(), version() |
140 | */ |
141 | QString QRemoteServiceRegister::Entry::serviceName() const |
142 | { |
143 | return d->service; |
144 | } |
145 | |
146 | /*! |
147 | Returns the version of the registration entry in format x.y. |
148 | |
149 | This should correspond to the interface version from the service XML description. |
150 | |
151 | \sa interfaceName(), serviceName() |
152 | */ |
153 | QString QRemoteServiceRegister::Entry::version() const |
154 | { |
155 | return d->ifaceVersion; |
156 | } |
157 | |
158 | const QMetaObject * QRemoteServiceRegister::Entry::metaObject() const |
159 | { |
160 | return d->meta; |
161 | } |
162 | |
163 | /*! |
164 | Sets the QRemoteServiceRegister::InstanceType of the registration entry. |
165 | |
166 | If this is not explicitly called, the default instance \a type for the registration entry |
167 | is QRemoteServiceRegister::PrivateInstance. |
168 | */ |
169 | void QRemoteServiceRegister::Entry::setInstantiationType(QRemoteServiceRegister::InstanceType type) |
170 | { |
171 | d->instanceType = type; |
172 | } |
173 | |
174 | /*! |
175 | Returns the QRemoteServiceRegister::InstanceType of the registration entry. |
176 | */ |
177 | QRemoteServiceRegister::InstanceType QRemoteServiceRegister::Entry::instantiationType() const |
178 | { |
179 | return d->instanceType; |
180 | } |
181 | |
182 | /*! |
183 | \class QRemoteServiceRegister |
184 | \inmodule QtServiceFramework |
185 | \ingroup servicefw |
186 | \brief The QRemoteServiceRegister class manages instances of remote service objects. |
187 | |
188 | This class registers and publishes IPC based service objects. It owns the service's |
189 | objects and uses the platform specific IPC mechanism to publish the service. |
190 | |
191 | In order for the remote services to be discoverable by QServiceManager each |
192 | QRemoteServiceRegister::Entry must be registered with the same information in |
193 | the XML description, otherwise no corresponding QServiceInterfaceDescriptor can be |
194 | found. |
195 | |
196 | The following XML descriptor is used for subsequent examples. |
197 | |
198 | \code |
199 | <SFW version="1.1"> |
200 | <service> |
201 | <name>MyService</name> |
202 | <ipcaddress>my_executable</ipcaddress> |
203 | <description>My service example</description> |
204 | <interface> |
205 | <name>com.nokia.qt.example.myService</name> |
206 | <version>1.0</version> |
207 | <description>My private service</description> |
208 | <capabilities></capabilities> |
209 | </interface> |
210 | </service> |
211 | </SFW> |
212 | \endcode |
213 | |
214 | The snippet belows demonstrates how an application can register the class \a MyClass |
215 | as a remote service, which is published and accessible to clients who wish to load |
216 | service object instances. |
217 | |
218 | \code |
219 | int main(int argc, char** argv) |
220 | { |
221 | QCoreApplication app(argc, argv); |
222 | |
223 | QRemoteServiceRegister *serviceRegister = new QRemoteServiceRegister(); |
224 | |
225 | QRemoteServiceRegister::Entry myService; |
226 | myService = serviceRegister->createEntry<MyClass>( |
227 | "MyService", "com.nokia.qt.example.myservice", "1.0"); |
228 | |
229 | serviceRegister->publishEntries("my_service"); |
230 | |
231 | return app.exec(); |
232 | delete serviceRegister; |
233 | } |
234 | \endcode |
235 | |
236 | By default all entries are created as \l QRemoteServiceRegister::PrivateInstance |
237 | types. This can be changed by calling QRemoteServiceRegister::Entry::setInstantiationType() |
238 | on the entry. Once the service register has been published the associated service entries |
239 | can no longer be changed. |
240 | |
241 | \sa QRemoteServiceRegister::Entry |
242 | */ |
243 | |
244 | /*! |
245 | \enum QRemoteServiceRegister::InstanceType |
246 | Defines the two types of instances for a registration entry |
247 | \value GlobalInstance New requests for a service gets the same service instance |
248 | \value PrivateInstance New requests for a service gets a new service instance |
249 | */ |
250 | |
251 | /*! |
252 | \fn void QRemoteServiceRegister::instanceClosed(const QRemoteServiceRegister::Entry& entry) |
253 | |
254 | This signal is emitted whenever a created instance has been closed. This indicates |
255 | that a connected client has either shutdown or released the loaded service object. |
256 | |
257 | \a entry is supplied to identify which registered service |
258 | entry the closed instance belonged to. |
259 | |
260 | \sa allInstancesClosed() |
261 | */ |
262 | |
263 | /*! |
264 | \fn void QRemoteServiceRegister::allInstancesClosed() |
265 | |
266 | This signal is emitted whenever all service instances have been closed. This indicates |
267 | that the last connected client has either shutdown or released the loaded service object. |
268 | |
269 | \sa instanceClosed() |
270 | */ |
271 | |
272 | /*! |
273 | \typedef QRemoteServiceRegister::CreateServiceFunc |
274 | \internal |
275 | Denotes a function pointer returning a service instance |
276 | */ |
277 | |
278 | /*! |
279 | \typedef QRemoteServiceRegister::SecurityFilter |
280 | \internal |
281 | Denotes a function pointer used for the security filter feature |
282 | */ |
283 | |
284 | /*! |
285 | Creates a service register instance with the given \a parent. |
286 | */ |
287 | QRemoteServiceRegister::QRemoteServiceRegister(QObject* parent) |
288 | : QObject(parent) |
289 | , d(0) |
290 | { |
291 | connect(sender: InstanceManager::instance(), SIGNAL(allInstancesClosed()), |
292 | receiver: this, SIGNAL(allInstancesClosed())); |
293 | connect(sender: InstanceManager::instance(), SIGNAL(instanceClosed(QRemoteServiceRegister::Entry)), |
294 | receiver: this, SIGNAL(instanceClosed(QRemoteServiceRegister::Entry))); |
295 | } |
296 | |
297 | /*! |
298 | Destroys the service register instance |
299 | */ |
300 | QRemoteServiceRegister::~QRemoteServiceRegister() |
301 | { |
302 | } |
303 | |
304 | /*! |
305 | Publishes every service QRemoteServiceRegister::Entry that has been created using |
306 | \l createEntry(). The \a ident is the service specific IPC address under which |
307 | the service can be reached. |
308 | |
309 | This address must match the address provided in the services XML descriptor, otherwise |
310 | the service will not be discoverable. In some cases this may also cause the IPC |
311 | rendezvous feature to fail. |
312 | |
313 | \sa createEntry() |
314 | */ |
315 | void QRemoteServiceRegister::publishEntries(const QString& ident) |
316 | { |
317 | if (!d) init(); |
318 | d->publishServices(ident); |
319 | } |
320 | |
321 | /*! |
322 | \property QRemoteServiceRegister::quitOnLastInstanceClosed |
323 | |
324 | \brief Terminate the service when all clients have closed all objects. Default value is true. |
325 | */ |
326 | bool QRemoteServiceRegister::quitOnLastInstanceClosed() const |
327 | { |
328 | if (!d) const_cast<QRemoteServiceRegister*>(this)->init(); |
329 | return d->quitOnLastInstanceClosed(); |
330 | } |
331 | |
332 | void QRemoteServiceRegister::setQuitOnLastInstanceClosed(bool quit) |
333 | { |
334 | if (!d) init(); |
335 | d->setQuitOnLastInstanceClosed(quit); |
336 | } |
337 | |
338 | /*! |
339 | \since 5.0 |
340 | |
341 | Set the user id for the socket or pipe. For backends that use sockets or |
342 | pipes and provide filesystem based access control. |
343 | |
344 | */ |
345 | |
346 | void QRemoteServiceRegister::setBaseUserIdentifier(qintptr uid) |
347 | { |
348 | if (!d) init(); |
349 | d->setBaseUserIdentifier(uid); |
350 | } |
351 | |
352 | /*! |
353 | \since 5.0 |
354 | |
355 | Get the user id set on the socket or pipe. |
356 | */ |
357 | |
358 | qintptr QRemoteServiceRegister::getBaseUserIdentifier() const |
359 | { |
360 | if (!d) const_cast<QRemoteServiceRegister*>(this)->init(); |
361 | return d->getBaseUserIdentifier(); |
362 | } |
363 | |
364 | /*! |
365 | \since 5.0 |
366 | |
367 | Set the group id for the socket or pipe. For backends that use sockets or |
368 | pipes and provide filesystem based access control. |
369 | */ |
370 | |
371 | void QRemoteServiceRegister::setBaseGroupIdentifier(qintptr gid) |
372 | { |
373 | if (!d) init(); |
374 | d->setBaseGroupIdentifier(gid); |
375 | } |
376 | |
377 | /*! |
378 | \since 5.0 |
379 | |
380 | Get the group id set on the socket or pipe. |
381 | */ |
382 | |
383 | qintptr QRemoteServiceRegister::getBaseGroupIdentifier() const |
384 | { |
385 | if (!d) const_cast<QRemoteServiceRegister*>(this)->init(); |
386 | return d->getBaseGroupIdentifier(); |
387 | } |
388 | |
389 | /*! |
390 | \since 5.0 |
391 | |
392 | Set the socket access control. This sets the file |
393 | system permissions on that socket. |
394 | |
395 | */ |
396 | |
397 | void QRemoteServiceRegister::setSecurityAccessOptions(SecurityAccessOptions options) |
398 | { |
399 | if (!d) init(); |
400 | d->setSecurityOptions(options); |
401 | } |
402 | |
403 | /*! |
404 | Allows a security filter to be set which can access |
405 | QRemoteServiceRegister::QRemoteServiceRegisterCredentials. |
406 | |
407 | The \a filter is a function pointer where the function code implements possible |
408 | permission checks and returns true or false. If a connecting client fails the security |
409 | filter it will be denied access and unable to obtain a valid service instance. |
410 | |
411 | The following snippet is an example of how to use the security filter feature. |
412 | |
413 | \code |
414 | bool myFunction(const void *p) |
415 | { |
416 | const QRemoteServiceRegisterCredentials *cred = |
417 | (const struct QRemoteServiceRegisterCredentials *)p; |
418 | |
419 | // allow the superuser |
420 | if (cred->uid == 0) |
421 | return true; |
422 | |
423 | return false; |
424 | } |
425 | |
426 | int main(int argc, char** argv) |
427 | { |
428 | ... |
429 | |
430 | QRemoteServiceRegister* serviceRegister = new QRemoteServiceRegister(); |
431 | service->setSecurityFilter(myFunction); |
432 | |
433 | ... |
434 | } |
435 | \endcode |
436 | |
437 | */ |
438 | QRemoteServiceRegister::SecurityFilter QRemoteServiceRegister::setSecurityFilter(QRemoteServiceRegister::SecurityFilter filter) |
439 | { |
440 | if (!d) init(); |
441 | return d->setSecurityFilter(filter); |
442 | } |
443 | |
444 | /*! |
445 | \fn QRemoteServiceRegister::createEntry(const QString& serviceName, const QString& interfaceName, const QString& version) |
446 | |
447 | Creates an entry on our remote instance manager. The \a serviceName, \a interfaceName and |
448 | \a version must match the service XML descriptor in order for the remote service to be |
449 | discoverable. |
450 | |
451 | \sa publishEntries() |
452 | */ |
453 | QRemoteServiceRegister::Entry QRemoteServiceRegister::createEntry(const QString& serviceName, const QString& interfaceName, const QString& version, QRemoteServiceRegister::CreateServiceFunc cptr, const QMetaObject* meta) |
454 | { |
455 | if (serviceName.isEmpty() |
456 | || interfaceName.isEmpty() |
457 | || version.isEmpty() ) { |
458 | qWarning() << "QRemoteServiceRegister::registerService: service name, interface name and version must be specified" ; |
459 | return Entry(); |
460 | } |
461 | |
462 | Entry e; |
463 | e.d->service = serviceName; |
464 | e.d->iface = interfaceName; |
465 | e.d->ifaceVersion = version; |
466 | e.d->cptr = cptr; |
467 | e.d->meta = meta; |
468 | |
469 | Q_ASSERT(InstanceManager::instance()); |
470 | InstanceManager::instance()->addType(entry: e); |
471 | |
472 | return e; |
473 | } |
474 | |
475 | bool QRemoteServiceRegister::event(QEvent *e) |
476 | { |
477 | if (!d && e->type() == QEvent::DynamicPropertyChange) { |
478 | QDynamicPropertyChangeEvent *event = static_cast<QDynamicPropertyChangeEvent*>(e); |
479 | if (event->propertyName() == QByteArray("serviceType" )) { |
480 | QService::Type serviceType = static_cast<QService::Type>(property(name: "serviceType" ).toInt()); |
481 | d = QRemoteServiceRegisterPrivate::constructPrivateObject(serviceType, parent: this); |
482 | } |
483 | } |
484 | |
485 | return QObject::event(event: e); |
486 | } |
487 | |
488 | void QRemoteServiceRegister::init() |
489 | { |
490 | d = QRemoteServiceRegisterPrivate::constructPrivateObject(parent: this); |
491 | } |
492 | |
493 | #ifndef QT_NO_DATASTREAM |
494 | Q_SERVICEFW_EXPORT QDataStream& operator>>(QDataStream& s, QRemoteServiceRegister::Entry& entry) { |
495 | //for now we only serialize version, iface and service name |
496 | //needs to sync with qHash and operator== |
497 | s >> entry.d->service >> entry.d->iface >> entry.d->ifaceVersion; |
498 | return s; |
499 | } |
500 | |
501 | Q_SERVICEFW_EXPORT QDataStream& operator<<(QDataStream& s, const QRemoteServiceRegister::Entry& entry) { |
502 | //for now we only serialize version, iface and service name |
503 | //needs to sync with qHash and operator== |
504 | s << entry.d->service << entry.d->iface << entry.d->ifaceVersion; |
505 | return s; |
506 | } |
507 | #endif |
508 | |
509 | #ifndef QT_NO_DEBUG_STREAM |
510 | QDebug operator<<(QDebug dbg, const QRemoteServiceRegister::Entry& entry) { |
511 | dbg.nospace() << "QRemoteServiceRegister::Entry(" |
512 | << entry.serviceName() << ", " |
513 | << entry.interfaceName() << ", " |
514 | << entry.version() << ")" ; |
515 | return dbg.space(); |
516 | } |
517 | #endif |
518 | |
519 | #include "moc_qremoteserviceregister.cpp" |
520 | |
521 | QT_END_NAMESPACE |
522 | |