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 <stdio.h>
35
36#include <QtCore>
37#include <QTextStream>
38#include <qservicemanager.h>
39#include <servicemetadata_p.h>
40#include <QString>
41#include <QDir>
42
43static const char * const errorTable[] = {
44 "No error", //0
45 "Storage read error",
46 "Invalid service location",
47 "Invalid service xml",
48 "Invalid service interface descriptor",
49 "Service already exists",
50 "Interface implementation already exists",
51 "Loading of plug-in failed",
52 "Service or interface not found",
53 "Insufficient capabilities to access service",
54 "Unknown error",
55};
56
57class CommandProcessor : public QObject
58{
59 Q_OBJECT
60
61public:
62 CommandProcessor(QObject *parent = 0);
63 ~CommandProcessor();
64
65 void execute(const QStringList &options, const QString &cmd, const QStringList &args);
66 void showUsage();
67 static void showUsage(QTextStream *stream);
68 int errorCode();
69
70public slots:
71 void browse(const QStringList &args);
72 void search(const QStringList &args);
73 void add(const QStringList &args);
74 void remove(const QStringList &args);
75 void dbusservice(const QStringList &args);
76 void setdefault(const QStringList &args);
77
78private:
79 bool setOptions(const QStringList &options);
80 void setErrorCode(int error);
81 void showAllEntries();
82 void showInterfaceInfo(const QServiceFilter &filter);
83 void showInterfaceInfo(QList<QServiceInterfaceDescriptor> descriptors);
84 void showServiceInfo(const QString &service);
85
86 QServiceManager *serviceManager;
87 QTextStream *stdoutStream;
88 int m_error;
89};
90
91CommandProcessor::CommandProcessor(QObject *parent)
92 : QObject(parent),
93 serviceManager(0),
94 stdoutStream(new QTextStream(stdout)),
95 m_error(0)
96{
97}
98
99CommandProcessor::~CommandProcessor()
100{
101 delete stdoutStream;
102}
103
104void CommandProcessor::execute(const QStringList &options, const QString &cmd, const QStringList &args)
105{
106 if (cmd.isEmpty()) {
107 *stdoutStream << "Error: no command given\n\n";
108 showUsage();
109 return;
110 }
111
112 if (!setOptions(options))
113 return;
114
115 int methodIndex = metaObject()->indexOfMethod(method: cmd.toLatin1() + "(QStringList)");
116 if (methodIndex < 0) {
117 *stdoutStream << "Bad command: " << cmd << "\n\n";
118 showUsage();
119 return;
120 }
121
122 if (!metaObject()->method(index: methodIndex).invoke(object: this, Q_ARG(QStringList, args)))
123 *stdoutStream << "Cannot invoke method for command:" << cmd << '\n';
124}
125
126void CommandProcessor::showUsage()
127{
128 showUsage(stream: stdoutStream);
129}
130
131void CommandProcessor::showUsage(QTextStream *stream)
132{
133 *stream << "Usage: servicefw [options] <command> [command parameters]\n\n"
134 "Commands:\n"
135 "\tbrowse List all registered services\n"
136 "\tsearch Search for a service or interface\n"
137 "\tadd Register a service\n"
138 "\tremove Unregister a service\n"
139 "\tdbusservice Generates a .service file for D-Bus service autostart\n"
140 "\n"
141 "Options:\n"
142 "\t--system Use the system-wide services database instead of the\n"
143 "\t user-specific database\n"
144 "\t--user Use the user-specific services database for add/remove.\n"
145 "\t This is the default\n"
146 "\n";
147}
148
149void CommandProcessor::browse(const QStringList &args)
150{
151 Q_UNUSED(args);
152 showAllEntries();
153}
154
155void CommandProcessor::setdefault(const QStringList &args)
156{
157 if (args.isEmpty() || args.count() < 2) {
158 *stdoutStream << "Usage:\n\tsetdefault <interface> <service>\n\n"
159 "Examples:\n"
160 "\tsetdefault com.nokia.SomeInterface foo";
161 return;
162 }
163
164
165 const QString &serviceInterface = args[0];
166 const QString &service = args[1];
167 bool res = serviceManager->setInterfaceDefault(service, interfaceName: serviceInterface);
168 if(!res) {
169 *stdoutStream << "Failed to set interface default"
170 << errorTable[serviceManager->error()];
171 }
172}
173
174void CommandProcessor::search(const QStringList &args)
175{
176 if (args.isEmpty()) {
177 *stdoutStream << "Usage:\n\tsearch <service|interface [version]>\n\n"
178 "Examples:\n"
179 "\tsearch SomeService\n"
180 "\tsearch com.nokia.SomeInterface\n"
181 "\tsearch com.nokia.SomeInterface 3.5\n"
182 "\tsearch com.nokia.SomeInterface 3.5+\n";
183 return;
184 }
185
186 const QString &name = args[0];
187 if (name.contains(c: '.')) {
188 QServiceFilter filter;
189 if (args.count() > 1) {
190 const QString &version = args[1];
191 bool minimumMatch = version.endsWith(c: '+');
192 filter.setInterface(interfaceName: name,
193 version: minimumMatch ? (version.mid(position: 0, n: version.length()-1)) : version,
194 rule: minimumMatch ? QServiceFilter::MinimumVersionMatch : QServiceFilter::ExactVersionMatch);
195 if (filter.interfaceName().isEmpty()) { // setInterface() failed
196 *stdoutStream << "Error: invalid interface version: " << version << '\n';
197 return;
198 }
199 } else {
200 filter.setInterface(interfaceName: name);
201 }
202 showInterfaceInfo(filter);
203 } else {
204 showServiceInfo(service: name);
205 }
206}
207
208
209void CommandProcessor::add(const QStringList &args)
210{
211 if (args.isEmpty()) {
212 *stdoutStream << "Usage:\n\tadd <service-xml-file>\n";
213 return;
214 }
215
216 const QString &xmlPath = args[0];
217 if (!QFile::exists(fileName: xmlPath)) {
218 *stdoutStream << "Error: cannot find file " << xmlPath << '\n';
219 setErrorCode(11);
220 return;
221 }
222
223 if (serviceManager->addService(xmlFilePath: xmlPath)) {
224 *stdoutStream << "Registered service at " << xmlPath << '\n';
225 } else {
226 int error = serviceManager->error();
227 if (error > 10) //map anything larger than 10 to 10
228 error = 10;
229 *stdoutStream << "Error: cannot register service at " << xmlPath
230 << " (" << errorTable[error] << ")" << '\n';
231
232 setErrorCode(error);
233 }
234}
235
236void CommandProcessor::remove(const QStringList &args)
237{
238 if (args.isEmpty()) {
239 *stdoutStream << "Usage:\n\tremove <service-name>\n";
240 return;
241 }
242
243 const QString &service = args[0];
244 if (serviceManager->removeService(serviceName: service))
245 *stdoutStream << "Unregistered service " << service << '\n';
246 else
247 *stdoutStream << "Error: cannot unregister service " << service << '\n';
248}
249
250void CommandProcessor::dbusservice(const QStringList &args)
251{
252 if (args.isEmpty() || args.size() == 1) {
253 *stdoutStream << "Usage:\n\tdbusservice <service-xml-file> <service-executable>\n";
254 return;
255 }
256
257#if defined(QT_NO_DBUS)
258 *stdoutStream << "Error: no D-Bus module found in Qt\n";
259 return;
260#endif
261
262 const QString &xmlPath = args[0];
263 if (!QFile::exists(fileName: xmlPath)) {
264 *stdoutStream << "Error: cannot find xml file at " << xmlPath << '\n';
265 return;
266 }
267
268 const QString &servicePath = args[1];
269 if (!QFile::exists(fileName: servicePath)) {
270 *stdoutStream << "Error: cannot find service file " << servicePath << '\n';
271 return;
272 }
273
274 QFile *f = new QFile(xmlPath);
275 ServiceMetaData parser(f);
276 if (!parser.extractMetadata()) {
277 *stdoutStream << "Error: invalid service xml at " << xmlPath << '\n';
278 return;
279 }
280
281 const ServiceMetaDataResults results = parser.parseResults();
282 if (results.type != QService::InterProcess) {
283 *stdoutStream << "Error: not an inter-process service xml at " << xmlPath << '\n';
284 return;
285 }
286
287 QString path;
288 if (serviceManager->scope() == QService::UserScope) {
289 // the d-bus xdg environment variable for the local service paths
290 const QByteArray xdgPath = qgetenv(varName: "XDG_DATA_HOME");
291 if (xdgPath.isEmpty()) {
292 // if not supplied generate in default
293 QDir dir(QDir::homePath());
294 dir.mkpath(dirPath: ".local/share/dbus-1/services/");
295 path = QDir::homePath() + "/.local/share/dbus-1/services/";
296 } else {
297 path = QString::fromLocal8Bit(str: xdgPath);
298 }
299 } else {
300 path = "/usr/share/dbus-1/services/";
301 }
302
303 const QString &name = "com.nokia.qtmobility.sfw." + results.name;
304 const QString &exec = QFileInfo(args[1]).absoluteFilePath();
305
306 QStringList names;
307 foreach (const QServiceInterfaceDescriptor &serviceInterface, results.interfaces) {
308 names << serviceInterface.interfaceName();
309 }
310 names.removeDuplicates();
311
312 for (int i = 0; i < names.size(); i++) {
313 const QString &file = path + names.at(i) + "." + results.name + ".service";
314 QFile data(file);
315 if (data.open(flags: QFile::WriteOnly)) {
316 QTextStream out(&data);
317 out << "[D-BUS Service]\n"
318 << "Name=" << name << '\n'
319 << "Exec=" << exec;
320 data.close();
321 } else {
322 *stdoutStream << "Error: cannot write to " << file << " (insufficient permissions)" << '\n';
323 return;
324 }
325
326 *stdoutStream << "Generated D-Bus autostart file " << file << '\n';
327 }
328}
329
330bool CommandProcessor::setOptions(const QStringList &options)
331{
332 if (serviceManager)
333 delete serviceManager;
334
335 QService::Scope scope = QService::UserScope;
336
337 QStringList opts = options;
338 QMutableListIterator<QString> i(opts);
339 while (i.hasNext()) {
340 QString option = i.next();
341 if (option == "--system") {
342 scope = QService::SystemScope;
343 i.remove();
344 } else if (option == "--user") {
345 scope = QService::UserScope;
346 i.remove();
347 }
348 }
349
350 if (!opts.isEmpty()) {
351 *stdoutStream << "Bad options: " << opts.join(sep: QLatin1Char(' ')) << "\n\n";
352 showUsage();
353 return false;
354 }
355
356 serviceManager = new QServiceManager(scope, this);
357
358 return true;
359}
360
361void CommandProcessor::showAllEntries()
362{
363 QStringList services = serviceManager->findServices();
364 if (services.isEmpty())
365 *stdoutStream << "No services found.\n";
366 else if (services.count() == 1)
367 *stdoutStream << "Found 1 service.\n\n";
368 else
369 *stdoutStream << "Found " << services.count() << " services.\n\n";
370
371 foreach (const QString &service, services)
372 showServiceInfo(service);
373}
374
375void CommandProcessor::showInterfaceInfo(const QServiceFilter &filter)
376{
377 QString serviceInterface = filter.interfaceName();
378 if (filter.majorVersion() >= 0 && filter.minorVersion() >= 0) {
379 serviceInterface += QString(" %1.%2").arg(a: filter.majorVersion()).arg(a: filter.minorVersion());
380 if (filter.versionMatchRule() == QServiceFilter::MinimumVersionMatch)
381 serviceInterface += '+';
382 }
383
384 QList<QServiceInterfaceDescriptor> descriptors = serviceManager->findInterfaces(filter);
385 if (descriptors.isEmpty()) {
386 *stdoutStream << "Interface " << serviceInterface << " not found.\n";
387 return;
388 }
389
390 *stdoutStream << serviceInterface << " is implemented by:\n";
391 foreach (const QServiceInterfaceDescriptor &desc, descriptors)
392 *stdoutStream << '\t' << desc.serviceName() << '\n';
393}
394
395void CommandProcessor::showServiceInfo(const QString &service)
396{
397 QList<QServiceInterfaceDescriptor> descriptors = serviceManager->findInterfaces(serviceName: service);
398 if (descriptors.isEmpty()) {
399 *stdoutStream << "Service " << service << " not found.\n";
400 return;
401 }
402
403 QList<QServiceInterfaceDescriptor> pluginDescs;
404 QList<QServiceInterfaceDescriptor> ipcDescs;
405 foreach (const QServiceInterfaceDescriptor &desc, descriptors) {
406 int serviceType = desc.attribute(which: QServiceInterfaceDescriptor::ServiceType).toInt();
407 if (serviceType == QService::Plugin)
408 pluginDescs.append(t: desc);
409 else
410 ipcDescs.append(t: desc);
411 }
412
413 if (pluginDescs.size() > 0) {
414 *stdoutStream << service;
415 if (ipcDescs.size() > 0)
416 *stdoutStream << " (Plugin):\n";
417 else
418 *stdoutStream << ":\n";
419
420 QString description = pluginDescs[0].attribute(
421 which: QServiceInterfaceDescriptor::ServiceDescription).toString();
422 if (!description.isEmpty())
423 *stdoutStream << '\t' << description << '\n';
424
425 *stdoutStream << "\tPlugin Library: ";
426 showInterfaceInfo(descriptors: pluginDescs);
427 }
428
429 if (ipcDescs.size() > 0) {
430 *stdoutStream << service;
431 if (pluginDescs.size() > 0)
432 *stdoutStream << " (IPC):\n";
433 else
434 *stdoutStream << ":\n";
435
436 QString description = ipcDescs[0].attribute(
437 which: QServiceInterfaceDescriptor::ServiceDescription).toString();
438 if (!description.isEmpty())
439 *stdoutStream << '\t' << description << '\n';
440
441 *stdoutStream << "\tIPC Address: ";
442 showInterfaceInfo(descriptors: ipcDescs);
443 }
444}
445
446void CommandProcessor::showInterfaceInfo(QList<QServiceInterfaceDescriptor> descriptors)
447{
448 QList<QServiceInterfaceDescriptor> systemList;
449 QList<QServiceInterfaceDescriptor> userList;
450
451 foreach (const QServiceInterfaceDescriptor &desc, descriptors) {
452 if (desc.scope() == QService::SystemScope)
453 systemList.append(t: desc);
454 else
455 userList.append(t: desc);
456 }
457
458 if (userList.size() > 0) {
459 *stdoutStream << userList[0].attribute(which: QServiceInterfaceDescriptor::Location).toString() << '\n'
460 << "\tUser Implements:\n";
461
462 foreach (const QServiceInterfaceDescriptor &desc, userList) {
463 QStringList capabilities = desc.attribute(
464 which: QServiceInterfaceDescriptor::Capabilities).toStringList();
465
466 *stdoutStream << "\t " << desc.interfaceName() << ' '
467 << desc.majorVersion() << '.' << desc.minorVersion()
468 << (capabilities.isEmpty() ? "" : "\t{" + capabilities.join(sep: ", ") + "}") << "\n";
469 }
470 }
471
472 if (systemList.size() > 0) {
473 if (userList.size() < 1)
474 *stdoutStream << systemList[0].attribute(which: QServiceInterfaceDescriptor::Location).toString() << '\n';
475
476 *stdoutStream << "\tSystem Implements:\n";
477
478 foreach (const QServiceInterfaceDescriptor &desc, systemList) {
479 QStringList capabilities = desc.attribute(
480 which: QServiceInterfaceDescriptor::Capabilities).toStringList();
481
482 *stdoutStream << "\t " << desc.interfaceName() << ' '
483 << desc.majorVersion() << '.' << desc.minorVersion()
484 << (capabilities.isEmpty() ? "" : "\t{" + capabilities.join(sep: ", ") + "}") << "\n";
485 }
486 }
487}
488
489int CommandProcessor::errorCode()
490{
491 return m_error;
492}
493
494void CommandProcessor::setErrorCode(int error)
495{
496 m_error = error;
497}
498
499int main(int argc, char *argv[])
500{
501 QCoreApplication app(argc, argv);
502 QStringList args = QCoreApplication::arguments();
503
504 // check for at least one non-option argument
505 bool exec = false;
506
507 QStringList options;
508 for (int i=1; i<args.count(); i++) {
509 if (args[i].startsWith(s: "--"))
510 options += args[i];
511 else
512 exec = true;
513 }
514
515 if (!exec || args.count() == 1 || args.value(i: 1) == "--help" || args.value(i: 1) == "-h") {
516 QTextStream stream(stdout);
517 CommandProcessor::showUsage(stream: &stream);
518 return 0;
519 }
520
521 CommandProcessor processor;
522 processor.execute(options, cmd: args.value(i: options.count() + 1), args: args.mid(pos: options.count() + 2));
523 return processor.errorCode();
524}
525
526#include "servicefw.moc"
527

source code of qtsystems/src/tools/servicefw/servicefw.cpp