ssu
Loading...
Searching...
No Matches
ssucli.cpp
Go to the documentation of this file.
1
8
9/*
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 *
23 */
24#include <QCoreApplication>
25
26#include <termios.h>
27#include <unistd.h>
28
34#include "constants.h"
35#include <QDebug>
36
37#include "ssucli.h"
38
39SsuCli::SsuCli()
40 : QObject()
41{
42 qDBusRegisterMetaType<SsuRepo>();
43
44 connect(this, SIGNAL(done()),
45 QCoreApplication::instance(), SLOT(quit()), Qt::DirectConnection);
46 connect(&ssu, SIGNAL(done()),
47 this, SLOT(handleResponse()));
48
49 ssuProxy = new SsuProxy();
50
51 connect(ssuProxy, SIGNAL(done()),
52 this, SLOT(handleDBusResponse()));
53
54 state = Idle;
55}
56
57SsuCli::~SsuCli()
58{
59 ssuProxy->quit();
60}
61
62void SsuCli::handleResponse()
63{
64 QTextStream qout(stdout);
65
66 if (ssu.error()) {
67 qout << "Last operation failed: \n" << ssu.lastError() << endl;
68 QCoreApplication::exit(1);
69 } else {
70 qout << "Operation successful (direct)" << endl;
71 QCoreApplication::exit(0);
72 }
73}
74
75void SsuCli::handleDBusResponse()
76{
77 QTextStream qout(stdout);
78
79 if (ssuProxy->error()) {
80 qout << "Last operation failed: \n" << ssuProxy->lastError() << endl;
81 QCoreApplication::exit(1);
82 } else {
83 qout << "Operation successful" << endl;
84 QCoreApplication::exit(0);
85 }
86}
87
88void SsuCli::optBrand(QStringList opt)
89{
90 QTextStream qout(stdout);
91
92 if (opt.count() == 3 && opt.at(2) == "-s") {
93 qout << ssu.brand();
94 state = Idle;
95 } else if (opt.count() == 2) {
96 qout << "Device brand is: " << ssu.brand() << endl;
97 state = Idle;
98 }
99}
100
101void SsuCli::optDomain(QStringList opt)
102{
103 QTextStream qout(stdout);
104 QTextStream qerr(stderr);
105
106 if (opt.count() == 3 && opt.at(2) == "-s") {
107 qout << ssu.domain();
108 state = Idle;
109 } else if (opt.count() > 2 && opt.at(2) == "-c") {
110 if (opt.count() == 3) { // dump all domain config
111 QVariantMap config = ssu.getDomainConfig(ssu.domain());
112 for (QVariantMap::iterator i = config.begin(); i != config.end(); i++) {
113 qout << i.key() << ": " << i.value().toString() << endl;
114 }
115 state = Idle;
116 } else if (opt.count() == 4) { // dump one domain config value
117 QVariantMap config = ssu.getDomainConfig(ssu.domain());
118 qout << config.value(opt.at(3)).toString() << endl;
119 state = Idle;
120 } else if (opt.count() == 5) { // set one domain config value
121 QVariantMap config = ssu.getDomainConfig(ssu.domain());
122 config.insert(opt.at(3), opt.at(4));
123 QDBusPendingReply<> reply = ssuProxy->setDomainConfig(ssu.domain(), config);
124 reply.waitForFinished();
125 if (reply.isError()) {
126 qerr << fallingBackToDirectUse(reply.error()) << endl;
127 ssu.setDomainConfig(ssu.domain(), config);
128 }
129 }
130 } else if (opt.count() == 3) {
131 qout << "Changing domain from " << ssu.domain() << " to " << opt.at(2) << endl;
132 ssu.setDomain(opt.at(2));
133
134 state = Idle;
135 } else if (opt.count() == 2) {
136 qout << "Device domain is currently: " << ssu.domain() << endl;
137 state = Idle;
138 }
139}
140
141void SsuCli::optFlavour(QStringList opt)
142{
143 QTextStream qout(stdout);
144 QTextStream qerr(stderr);
145
146 if (opt.count() == 3 && opt.at(2) == "-s") {
147 qout << ssu.flavour();
148 state = Idle;
149 } else if (opt.count() == 3) {
150 qout << "Changing flavour from " << ssu.flavour()
151 << " to " << opt.at(2) << endl;
152
153 QDBusPendingReply<> reply = ssuProxy->setFlavour(opt.at(2));
154 reply.waitForFinished();
155 if (reply.isError()) {
156 qerr << fallingBackToDirectUse(reply.error()) << endl;
157 ssu.setFlavour(opt.at(2));
158
159 SsuRepoManager repoManager;
160 repoManager.update();
161 uidWarning();
162 }
163
164 state = Idle;
165 } else if (opt.count() == 2) {
166 qout << "Device flavour is currently: " << ssu.flavour() << endl;
167 state = Idle;
168 }
169}
170
171QString SsuCli::getModeString(int mode) {
172 QStringList modeList;
173
175 modeList.append("DisableRepoManager");
176 if ((mode & Ssu::RndMode) == Ssu::RndMode)
177 modeList.append("RndMode");
178 if ((mode & Ssu::ReleaseMode) == Ssu::ReleaseMode)
179 modeList.append("ReleaseMode");
180 if ((mode & Ssu::LenientMode) == Ssu::LenientMode)
181 modeList.append("LenientMode");
182 if ((mode & Ssu::UpdateMode) == Ssu::UpdateMode)
183 modeList.append("UpdateMode");
185 modeList.append("AppInstallMode");
186
187 return modeList.join(" | ");
188}
189
190void SsuCli::optMode(QStringList opt)
191{
192 QTextStream qout(stdout);
193 QTextStream qerr(stderr);
194
195 int deviceMode = ssu.deviceMode();
196
197 if (opt.count() == 2) {
198 qout << "Device mode is: " << deviceMode
199 << " (" << getModeString(deviceMode) << ")" << endl;
200
201 if ((deviceMode & Ssu::RndMode) == Ssu::RndMode &&
202 (deviceMode & Ssu::ReleaseMode) == Ssu::ReleaseMode)
203 qout << "Both Release and RnD mode set, device is in RnD mode" << endl;
204
205 state = Idle;
206 } else if (opt.count() == 3 && opt.at(2) == "-s") {
207 qout << deviceMode;
208 state = Idle;
209 return;
210 } else if (opt.count() >= 3) {
211 bool isInt;
212 int newMode = opt.at(2).toInt(&isInt);
213
214 if (!isInt) {
215 for (int i=2; i<opt.size(); i++) {
216 if (opt.at(i) == "DisableRepoManager")
217 newMode |= Ssu::DisableRepoManager;
218 else if (opt.at(i) == "RndMode")
219 newMode |= Ssu::RndMode;
220 else if (opt.at(i) == "ReleaseMode")
221 newMode |= Ssu::ReleaseMode;
222 else if (opt.at(i) == "LenientMode")
223 newMode |= Ssu::LenientMode;
224 else if (opt.at(i) == "UpdateMode")
225 newMode |= Ssu::UpdateMode;
226 else if (opt.at(i) == "AppInstallMode")
227 newMode |= Ssu::AppInstallMode;
228 else {
229 qout << "Unknown mode: " << opt.at(i) << endl;
230 state = Idle;
231 return;
232 }
233 }
234 }
235
236 qout << "Setting device mode from " << deviceMode
237 << " (" << getModeString(deviceMode)
238 << ") to " << newMode
239 << " (" << getModeString(newMode) << ")" << endl;
240
241 QDBusPendingReply<> reply = ssuProxy->setDeviceMode(newMode);
242 reply.waitForFinished();
243 if (reply.isError()) {
244 qerr << fallingBackToDirectUse(reply.error()) << endl;
245 ssu.setDeviceMode(Ssu::DeviceModeFlags(newMode));
246
247 SsuRepoManager repoManager;
248 repoManager.update();
249 uidWarning();
250 }
251 }
252
253 state = Idle;
254}
255
256void SsuCli::optModel(QStringList opt)
257{
258 QTextStream qout(stdout);
259 SsuDeviceInfo deviceInfo;
260
261 if (opt.count() == 3 && opt.at(2) == "-s") {
262 qout << deviceInfo.deviceModel();
263 state = Idle;
264 } else if (opt.count() == 2) {
265 qout << "Device model is: " << deviceInfo.deviceModel() << endl;
266 state = Idle;
267 }
268}
269
270void SsuCli::optModifyRepo(enum Actions action, QStringList opt)
271{
272 SsuRepoManager repoManager;
273 QStringList systemRepos = repoManager.repos(Ssu::BoardFilter | Ssu::Available);
274 QTextStream qerr(stderr);
275
276 if (opt.count() == 3) {
277 // Repo without URL must be defined in config
278 if (action == Add && !systemRepos.contains(opt.at(2))) {
279 qerr << "Repository not defined: " << opt.at(2) << endl;
280 return;
281 }
282 // Don't add a repo if it is already present
283 if (action == Add && repoManager.repos(Ssu::NoFilter).contains(opt.at(2))) {
284 qerr << "Repository already added: " << opt.at(2) << endl;
285 return;
286 }
287 // Refuse to remove default repos except in DisableRepoManager mode
288 if (action == Remove && ((ssu.deviceMode() & Ssu::DisableRepoManager) != Ssu::DisableRepoManager)
289 && repoManager.repos(Ssu::BoardFilter).contains(opt.at(2))) {
290 qerr << "Cannot remove default repository: " << opt.at(2) << endl;
291 return;
292 }
293
294 QDBusPendingReply<> reply = ssuProxy->modifyRepo(action, opt.at(2));
295 reply.waitForFinished();
296 if (reply.isError()) {
297 qerr << fallingBackToDirectUse(reply.error()) << endl;
298
299 switch (action) {
300 case Add:
301 repoManager.add(opt.at(2));
302 repoManager.update();
303 uidWarning();
304 break;
305 case Remove:
306 repoManager.remove(opt.at(2));
307 repoManager.update();
308 uidWarning();
309 break;
310 case Disable:
311 repoManager.disable(opt.at(2));
312 repoManager.update();
313 uidWarning();
314 break;
315 case Enable:
316 repoManager.enable(opt.at(2));
317 repoManager.update();
318 uidWarning();
319 break;
320 }
321 }
322 } else if (opt.count() == 4 && action == Add) {
323 QString url, repo;
324
325 if (opt.at(2).indexOf(QRegExp("[a-z]*://", Qt::CaseInsensitive)) == 0) {
326 url = opt.at(2);
327 repo = opt.at(3);
328 } else if (opt.at(3).indexOf(QRegExp("[a-z]*://", Qt::CaseInsensitive)) == 0) {
329 url = opt.at(3);
330 repo = opt.at(2);
331 } else {
332 qerr << "Invalid parameters for 'ssu ar': URL required." << endl;
333 return;
334 }
335
336 if (systemRepos.contains(repo)) {
337 qerr << "Cannot override system repository: " << repo << endl;
338 return;
339 }
340
341 // Don't add a repo if it is already present
342 if (action == Add && repoManager.repos(Ssu::NoFilter).contains(repo)) {
343 qerr << "Repository already added: " << repo << endl;
344 return;
345 }
346
347 QDBusPendingReply<> reply = ssuProxy->addRepo(repo, url);
348 reply.waitForFinished();
349 if (reply.isError()) {
350 qerr << fallingBackToDirectUse(reply.error()) << endl;
351 repoManager.add(repo, url);
352 repoManager.update();
353 uidWarning();
354 }
355 }
356}
357
358void SsuCli::optRegister(QStringList opt)
359{
360 /*
361 * register a new device
362 */
363
364 QString username, password;
365 QTextStream qin(stdin);
366 QTextStream qout(stdout);
367 QTextStream qerr(stderr);
368 SsuCoreConfig *ssuSettings = SsuCoreConfig::instance();
369
370 struct termios termNew, termOld;
371
372 qout << "Username: " << flush;
373 username = qin.readLine();
374
375 tcgetattr(STDIN_FILENO, &termNew);
376 termOld = termNew;
377 termNew.c_lflag &= ~ECHO;
378 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &termNew) == -1)
379 qout << "WARNING: Unable to disable echo on your terminal, password will echo!" << endl;
380
381 qout << "Password: " << flush;
382 password = qin.readLine();
383 qout << endl;
384
385 tcsetattr(STDIN_FILENO, TCSANOW, &termOld);
386
387 if (opt.count() == 3 && opt.at(2) == "-h")
388 ssuSettings->setValue("repository-url-variables/user", username);
389
390 QDBusPendingReply<> reply = ssuProxy->registerDevice(username, password);
391 reply.waitForFinished();
392 if (reply.isError()) {
393 qerr << fallingBackToDirectUse(reply.error()) << endl;
394 ssu.sendRegistration(username, password);
395 }
396
397 state = Busy;
398}
399
400void SsuCli::optRelease(QStringList opt)
401{
402 QTextStream qout(stdout);
403 QTextStream qerr(stderr);
404
405 if (opt.count() == 3) {
406 if (opt.at(2) == "-r") {
407 qout << "Device release (RnD) is currently: " << ssu.release(true) << endl;
408 state = Idle;
409 } else {
410 qout << "Changing release from " << ssu.release()
411 << " to " << opt.at(2) << endl;
412 qout << "Your device is now in release mode!" << endl;
413
414 QDBusPendingReply<> reply = ssuProxy->setRelease(opt.at(2), false);
415 reply.waitForFinished();
416 if (reply.isError()) {
417 qerr << fallingBackToDirectUse(reply.error()) << endl;
418 ssu.setRelease(opt.at(2));
419
420 SsuRepoManager repoManager;
421 repoManager.update();
422 uidWarning();
423 }
424
425 state = Idle;
426 }
427 } else if (opt.count() == 2) {
428 qout << "Device release is currently: " << ssu.release() << endl;
429 state = Idle;
430 } else if (opt.count() == 4 && opt.at(2) == "-r") {
431 qout << "Changing release (RnD) from " << ssu.release(true)
432 << " to " << opt.at(3) << endl;
433 qout << "Your device is now in RnD mode!" << endl;
434
435 QDBusPendingReply<> reply = ssuProxy->setRelease(opt.at(3), true);
436 reply.waitForFinished();
437 if (reply.isError()) {
438 qerr << fallingBackToDirectUse(reply.error()) << endl;
439 ssu.setRelease(opt.at(3), true);
440
441 SsuRepoManager repoManager;
442 repoManager.update();
443 uidWarning();
444 }
445
446 state = Idle;
447 }
448}
449
450void SsuCli::optRepos(QStringList opt)
451{
452 QTextStream qout(stdout);
453 QTextStream qerr(stderr);
454 SsuRepoManager repoManager;
455 SsuDeviceInfo deviceInfo;
456 QHash<QString, QString> repoParameters, repoOverride;
457 QString device;
458 bool rndRepo = false;
459 int micMode = 0, flagStart = 0;
460
461 if ((ssu.deviceMode() & Ssu::RndMode) == Ssu::RndMode)
462 rndRepo = true;
463
464 if (opt.count() >= 3 && opt.at(2) == "-m") {
465 micMode = 1;
466 // TODO: read the default mic override variables from some config
467 /*
468 repoOverride.insert("release", "@RELEASE@");
469 repoOverride.insert("flavour", "@FLAVOUR@");
470 repoOverride.insert("arch", "@ARCH@");
471 */
472 }
473
474 if (opt.count() >= 3 + micMode) {
475 // first argument is flag
476 if (opt.at(2 + micMode).contains("=")) {
477 flagStart = 2 + micMode;
478 } else if (!opt.at(2 + micMode).contains("=") &&
479 opt.count() == 3 + micMode) {
480 // first (and only) argument is device)
481 device = opt.at(2 + micMode);
482 } else if (!opt.at(2 + micMode).contains("=") &&
483 opt.count() > 3 + micMode &&
484 opt.at(3 + micMode).contains("=")) {
485 // first argument is device, second flag
486 device = opt.at(2 + micMode);
487 flagStart = 3 + micMode;
488 } else {
489 state = UserError;
490 return;
491 }
492 }
493
494 if (flagStart != 0) {
495 for (int i = flagStart; i < opt.count(); i++) {
496 if (opt.at(i).count("=") != 1) {
497 qout << "Invalid flag: " << opt.at(i) << endl;
498 state = Idle;
499 return;
500 }
501 QStringList split = opt.at(i).split("=");
502 repoOverride.insert(split.at(0), split.at(1));
503 }
504 }
505
506 if (repoOverride.contains("rnd")) {
507 if (repoOverride.value("rnd") == "true")
508 rndRepo = true;
509 else if (repoOverride.value("rnd") == "false")
510 rndRepo = false;
511 }
512
513 if (!device.isEmpty()) {
514 deviceInfo.setDeviceModel(device);
515 repoOverride.insert("model", device);
516 }
517
518 // TODO: rnd mode override needs to be implemented
519 QStringList repos;
520
521 // micMode? handle it and return, as it's a lot simpler than full mode
522 if (micMode) {
523 repos = repoManager.repos(rndRepo, deviceInfo, Ssu::BoardFilter);
524 foreach (const QString &repo, repos) {
525 QString repoName = repo;
526 if (repo.endsWith("-debuginfo")) {
527 repoName = repo.left(repo.size() - 10);
528 repoParameters.insert("debugSplit", "debug");
529 } else if (repoParameters.value("debugSplit") == "debug") {
530 repoParameters.remove("debugSplit");
531 }
532
533 QString repoUrl = ssu.repoUrl(repoName, rndRepo, repoParameters, repoOverride);
534 qout << "repo --name=" << repo << "-"
535 << repoOverride.value("release")
536 << " --baseurl=" << repoUrl << endl;
537 }
538 state = Idle;
539 return;
540 }
541
542 bool hasRepos = false;
543
544 if (device.isEmpty()) {
545 repos = repoManager.repos(rndRepo, deviceInfo, Ssu::BoardFilter | Ssu::UserBlacklist);
546 } else {
547 qout << "Printing repository configuration for '" << device << "'" << endl << endl;
548 repos = repoManager.repos(rndRepo, deviceInfo, Ssu::BoardFilter);
549 }
550
551 SsuCoreConfig *ssuSettings = SsuCoreConfig::instance();
552
553 if (!repos.isEmpty()) {
554 qout << "Enabled repositories (global): " << endl;
555 }
556 for (int i = 0; i <= 3; i++) {
557 // for each repository, print repo and resolve url
558 int longestField = 0;
559 foreach (const QString &repo, repos)
560 if (repo.length() > longestField)
561 longestField = repo.length();
562
563 qout.setFieldAlignment(QTextStream::AlignLeft);
564
565 foreach (const QString &repo, repos) {
566 hasRepos = true;
567 QString repoName = repo;
568 if (repo.endsWith("-debuginfo")) {
569 repoName = repo.left(repo.size() - 10);
570 repoParameters.insert("debugSplit", "debug");
571 } else if (repoParameters.value("debugSplit") == "debug")
572 repoParameters.remove("debugSplit");
573
574 QString repoUrl = ssu.repoUrl(repoName, rndRepo, repoParameters, repoOverride);
575 qout << " - " << qSetFieldWidth(longestField) << repo << qSetFieldWidth(0) << " ... " << repoUrl << endl;
576 }
577
578 if (i == 0) {
579 if (!device.isEmpty()) {
580 repos.clear();
581 continue;
582 }
583 repos = repoManager.repos(rndRepo, deviceInfo, Ssu::UserFilter | Ssu::UserBlacklist);
584 if (!repos.isEmpty()) {
585 qout << endl << "Enabled repositories (user): " << endl;
586 }
587 } else if (i == 1) {
588 repos = deviceInfo.disabledRepos();
589 if (!repos.isEmpty()) {
590 if (device.isEmpty())
591 qout << endl << "Disabled repositories (global, might be overridden by user config): " << endl;
592 else
593 qout << endl << "Disabled repositories (global): " << endl;
594 }
595 } else if (i == 2) {
596 repos.clear();
597 if (!device.isEmpty())
598 continue;
599 if (ssuSettings->contains("disabled-repos"))
600 repos.append(ssuSettings->value("disabled-repos").toStringList());
601 if (!repos.isEmpty()) {
602 qout << endl << "Disabled repositories (user): " << endl;
603 }
604 }
605 }
606
607 if (!hasRepos)
608 qerr << "No repositories defined" << endl;
609
610 state = Idle;
611}
612
613void SsuCli::optSet(QStringList opt)
614{
615 QTextStream qout(stdout);
616 SsuVariables var;
617 SsuCoreConfig *settings = SsuCoreConfig::instance();
618 QHash<QString, QString> storageHash;
619
620 // set repository specific variable
621 if (opt.count() == 5 && opt.at(2) == "-r") {
622 settings->setValue("repository-url-variables/" + opt.at(3), opt.at(4));
623 // clear repo specific variable
624 } else if (opt.count() == 4 && opt.at(2) == "-r") {
625 settings->remove("repository-url-variables/" + opt.at(3));
626 // list repo specific variables
627 } else if (opt.count() == 3 && opt.at(2) == "-r") {
628 qout << "Repository specific variables:" << endl << endl;
629 var.variableSection(settings, "repository-url-variables", &storageHash);
630 // set global variable
631 } else if (opt.count() == 4) {
632 settings->setValue("global-variables/" + opt.at(2), opt.at(3));
633 // clear global variable
634 } else if (opt.count() == 3) {
635 settings->remove("global-variables/" + opt.at(2));
636 // list global variables
637 } else if (opt.count() == 2) {
638 qout << "Global variables:" << endl << endl;
639 var.variableSection(settings, "global-variables", &storageHash);
640 }
641
642 settings->sync();
643
644 if (!storageHash.isEmpty()) {
645 QHash<QString, QString>::const_iterator i = storageHash.constBegin();
646 while (i != storageHash.constEnd()) {
647 qout << i.key() << "=" << i.value() << endl;
648 i++;
649 }
650 }
651
652 state = Idle;
653}
654
655void SsuCli::optStatus(QStringList opt)
656{
657 Q_UNUSED(opt)
658
659 QTextStream qout(stdout);
660 QTextStream qerr(stderr);
661 SsuDeviceInfo deviceInfo;
662
663 /*
664 * print device information and registration status
665 */
666
667 QString deviceUid;
668
669 QDBusPendingReply<QString> reply = ssuProxy->deviceUid();
670 reply.waitForFinished();
671 if (reply.isError()) {
672 qerr << "DBus unavailable, UUID not necessarily connected to reality." << endl;
673 deviceUid = deviceInfo.deviceUid();
674 } else
675 deviceUid = reply.value();
676
677 qout << "Device registration status: "
678 << (ssu.isRegistered() ? "registered" : "not registered") << endl;
679 qout << "Device model: " << deviceInfo.displayName(Ssu::DeviceModel) << " ("
680 << deviceInfo.deviceModel() << " / "
681 << deviceInfo.displayName(Ssu::DeviceDesignation) << ")" << endl;
682 if (!deviceInfo.deviceVariant().isEmpty())
683 qout << "Device variant: " << deviceInfo.deviceVariant() << endl;
684 qout << "Device UID: " << deviceUid << endl;
685 if ((ssu.deviceMode() & Ssu::RndMode) == Ssu::RndMode)
686 qout << "Release (rnd): " << ssu.release(true) << " (" << ssu.flavour() << ")" << endl;
687 else
688 qout << "Release: " << ssu.release() << endl;
689 qout << "Domain: " << ssu.domain() << endl;
690 qout << "Brand: " << (ssu.brand().isEmpty() ? "N/A" : ssu.brand()) << endl;
691
692 SsuCoreConfig *settings = SsuCoreConfig::instance();
693 QHash<QString, QString> storageHash;
694 SsuVariables var;
695 var.variableSection(settings, "global-variables", &storageHash);
696 if (!storageHash.isEmpty()) {
697 qout << "Global variables: " << endl;
698 QHash<QString, QString>::const_iterator i = storageHash.constBegin();
699 while (i != storageHash.constEnd()) {
700 qout << i.key() << "=" << i.value() << endl;
701 i++;
702 }
703 }
704}
705
706void SsuCli::optUpdateCredentials(QStringList opt)
707{
708 QTextStream qout(stdout);
709 /*
710 * update the credentials
711 * optional argument: -f
712 */
713 bool force = false;
714 if (opt.count() == 3 && opt.at(2) == "-f")
715 force = true;
716
717 if (!ssu.isRegistered()) {
718 qout << "Device is not registered, can't update credentials" << endl;
719 state = Idle;
720 QCoreApplication::exit(1);
721 } else {
722 ssu.updateCredentials(force);
723 state = Busy;
724 }
725}
726
727void SsuCli::optUpdateRepos(QStringList opt)
728{
729 Q_UNUSED(opt)
730
731 QTextStream qerr(stdout);
732
733 QDBusPendingReply<> reply = ssuProxy->updateRepos();
734 reply.waitForFinished();
735 if (reply.isError()) {
736 qerr << fallingBackToDirectUse(reply.error()) << endl;
737 SsuRepoManager repoManager;
738 repoManager.update();
739 uidWarning();
740 }
741}
742
743void SsuCli::run()
744{
745 QTextStream qerr(stderr);
746
747 QStringList arguments = QCoreApplication::arguments();
748
749 SsuCoreConfig *ssuSettings = SsuCoreConfig::instance();
750 if (!ssuSettings->isWritable())
751 qerr << "WARNING: ssu.ini does not seem to be writable. Setting values might not work." << endl;
752
753 // make sure there's a first argument to parse
754 if (arguments.count() < 2) {
755 usage();
756 return;
757 }
758
759 struct {
760 const char *longopt; // long option name
761 const char *shortopt; // option shortcut name
762 int minargs; // minimum number of required args
763 int maxargs; // -1 for "function will handle max args"
764 void (SsuCli::*handler)(QStringList opt); // handler function
765 } handlers[] = {
766 // functions accepting no additional arguments
767 "status", "s", 0, 0, &SsuCli::optStatus,
768 "updaterepos", "ur", 0, 0, &SsuCli::optUpdateRepos,
769
770 // functions requiring at least one argument
771 "addrepo", "ar", 1, 2, &SsuCli::optAddRepo,
772 "removerepo", "rr", 1, 1, &SsuCli::optRemoveRepo,
773 "enablerepo", "er", 1, 1, &SsuCli::optEnableRepo,
774 "disablerepo", "dr", 1, 1, &SsuCli::optDisableRepo,
775
776 // functions accepting 0 or more arguments
777 // those need to set state to Idle on success
778 "register", "r", 0, -1, &SsuCli::optRegister,
779 "repos", "lr", 0, -1, &SsuCli::optRepos,
780 "brand", "b", 0, -1, &SsuCli::optBrand,
781 "flavour", "fl", 0, -1, &SsuCli::optFlavour,
782 "mode", "m", 0, -1, &SsuCli::optMode,
783 "model", "mo", 0, -1, &SsuCli::optModel,
784 "release", "re", 0, -1, &SsuCli::optRelease,
785 "update", "up", 0, -1, &SsuCli::optUpdateCredentials,
786 "domain", "do", 0, -1, &SsuCli::optDomain,
787 "set", "set", 0, -1, &SsuCli::optSet,
788 };
789
790 bool found = false;
791 int argc = arguments.count() - 2;
792
793 for (unsigned int i = 0; i < sizeof(handlers) / sizeof(handlers[0]); i++) {
794 if ((arguments.at(1) != handlers[i].longopt) &&
795 (arguments.at(1) != handlers[i].shortopt)) {
796 continue;
797 }
798
799 if (argc < handlers[i].minargs) {
800 usage(QString("%1: Too few arguments").arg(handlers[i].longopt));
801 return;
802 }
803
804 if (handlers[i].maxargs != -1 && argc > handlers[i].maxargs) {
805 usage(QString("%1: Too many arguments").arg(handlers[i].longopt));
806 return;
807 }
808
809 // Call option handler
810 (this->*(handlers[i].handler))(arguments);
811
812 found = true;
813 break;
814 }
815
816 if (!found)
817 state = UserError;
818
819 // functions that need to wait for a response from ssu should set a flag so
820 // we can do default exit catchall here
821 if (state == Idle)
822 QCoreApplication::exit(0);
823 else if (state == UserError)
824 usage();
825}
826
827void SsuCli::uidWarning()
828{
829 if (geteuid() != 0) {
830 QTextStream qout(stderr);
831 qout << "You're not root. Run 'ssu ur' as root to recreate repository files" << endl;
832 }
833}
834
835void SsuCli::usage(const QString &message)
836{
837 QTextStream qout(stderr);
838 qout << "\nUsage: ssu <command> [-command-options] [arguments]" << endl
839 << endl
840 << "Repository management:" << endl
841 << "\tmode, m \tShow repository mode information." << endl
842 << "\t <int> \tSet repository mode as an integer (see docs)." << endl
843 << "\t <modes> \tCombo of modes to set from: DisableRepoManager RndMode " << endl
844 << "\t \tReleaseMode LenientMode UpdateMode AppInstallMode" << endl
845 << "\tupdaterepos, ur \tupdate repository files" << endl
846 << "\trepos, lr \tlist configured repositories" << endl
847 << "\t [-m] \tformat output suitable for kickstart" << endl
848 << "\t [device] \tuse repos for 'device'" << endl
849 << "\t [flags] \tadditional flags" << endl
850 << "\t rnd=<bool> \tset rnd or release mode (default: take from host)" << endl
851 << "\taddrepo, ar <repo> \tadd this repository" << endl
852 << "\t [url] \tspecify URL, if not configured" << endl
853 << "\tremoverepo, rr <repo> \tremove this repository from configuration" << endl
854 << "\tenablerepo, er <repo> \tenable this repository" << endl
855 << "\tdisablerepo, dr <repo> \tdisable this repository" << endl
856 << endl
857 << "Configuration management:" << endl
858 << "\tflavour, fl \tdisplay flavour used (RnD only)" << endl
859 << "\t [newflavour] \tset new flavour" << endl
860 << "\trelease, re \tdisplay release used" << endl
861 << "\t [-r] \tuse RnD release" << endl
862 << "\t [newrelease] \tset new (RnD)release" << endl
863 << "\tset \tdisplay global variables" << endl
864 << "\t [-r] \toperate on repository only variables" << endl
865 << "\t <variable> \tdisplay value of <variable>" << endl
866 << "\t <variable> <value> \tset value of <variable> to <value>" << endl
867 << "\tdomain do \tdisplay current device domain" << endl
868 << "\t [newdomain] \tset new domain" << endl
869 << "\t [-c] \tshow domain configuration" << endl
870 << "\t -c <variable> \tshow single domain variable" << endl
871 << "\t -c <variable> <val>\tset single domain variable" << endl
872 << endl
873 << "Device management:" << endl
874 << "\tstatus, s \tprint registration status and device information" << endl
875 << "\tregister, r \tregister this device" << endl
876 << "\t [-h] \tconfigure user for OBS home" << endl
877 << "\tupdate, up \tupdate repository credentials" << endl
878 << "\t [-f] \tforce update" << endl
879 << "\tmodel, mo \tprint name of device model (like N9)" << endl
880 << "\tbrand, b \tprint brand of device model" << endl
881 << endl;
882 if (!message.isEmpty())
883 qout << message << endl;
884 qout.flush();
885 QCoreApplication::exit(1);
886}
887
888QString SsuCli::fallingBackToDirectUse(const QDBusError &reason) const
889{
890 if (reason.type() == QDBusError::Disconnected)
891 return QStringLiteral("DBus unavailable, falling back to libssu");
892 else
893 return QStringLiteral("WARNING: DBus call failed, falling back to libssu: ") + reason.message();
894}
Q_INVOKABLE QString deviceUid()
QStringList disabledRepos()
Q_INVOKABLE void setDeviceModel(const QString &model=QString())
Q_INVOKABLE QString deviceVariant(bool fallback=false)
Q_INVOKABLE QString displayName(int type)
Q_INVOKABLE QString deviceModel()
int remove(const QString &repo)
int add(const QString &repo, const QString &repoUrl=QString())
QStringList repos(int filter=Ssu::NoFilter|Ssu::UserBlacklist)
int enable(const QString &repo)
int disable(const QString &repo)
void variableSection(const QString &section, QHash< QString, QString > *storageHash)
@ DisableRepoManager
Disable automagic repository management.
Definition ssu.h:68
@ ReleaseMode
Enable Release mode.
Definition ssu.h:70
@ RndMode
Enable RnD mode for device.
Definition ssu.h:69
@ UpdateMode
Do repo isolation and similar bits important for updating devices.
Definition ssu.h:72
@ AppInstallMode
Do repo isolation, but keep store repository enabled.
Definition ssu.h:73
@ LenientMode
Disable strict mode (i.e., keep unmanaged repositories).
Definition ssu.h:71
@ DeviceModel
Marketed device name, like Pogoblaster 3000. Board mappings key "prettyModel".
Definition ssu.h:84
@ DeviceDesignation
Type designation, like NCC-1701. Beard mappings key "deviceDesignation".
Definition ssu.h:85
@ UserFilter
Only user configured repositories.
Definition ssu.h:50
@ NoFilter
All repositories (global | user).
Definition ssu.h:52
@ UserBlacklist
User blacklist applied.
Definition ssu.h:53
@ BoardFilter
Only global repositories.
Definition ssu.h:51
@ Available
Include all defined repos, including disabled.
Definition ssu.h:54