24 #include <QCoreApplication>
41 qDBusRegisterMetaType<SsuRepo>();
43 connect(
this, SIGNAL(done()),
44 QCoreApplication::instance(), SLOT(quit()), Qt::DirectConnection);
45 connect(&ssu, SIGNAL(done()),
46 this, SLOT(handleResponse()));
50 connect(ssuProxy, SIGNAL(done()),
51 this, SLOT(handleDBusResponse()));
61 void SsuCli::handleResponse()
63 QTextStream qout(stdout);
66 qout <<
"Last operation failed: \n" << ssu.
lastError() << endl;
67 QCoreApplication::exit(1);
69 qout <<
"Operation successful (direct)" << endl;
70 QCoreApplication::exit(0);
74 void SsuCli::handleDBusResponse()
76 QTextStream qout(stdout);
78 if (ssuProxy->error()) {
79 qout <<
"Last operation failed: \n" << ssuProxy->lastError() << endl;
80 QCoreApplication::exit(1);
82 qout <<
"Operation successful" << endl;
83 QCoreApplication::exit(0);
87 void SsuCli::optBrand(QStringList opt)
89 QTextStream qout(stdout);
91 if (opt.count() == 3 && opt.at(2) ==
"-s") {
94 }
else if (opt.count() == 2) {
95 qout <<
"Device brand is: " << ssu.
brand() << endl;
100 void SsuCli::optDomain(QStringList opt)
102 QTextStream qout(stdout);
103 QTextStream qerr(stderr);
105 if (opt.count() == 3 && opt.at(2) ==
"-s") {
106 qout << ssu.domain();
108 }
else if (opt.count() > 2 && opt.at(2) ==
"-c") {
109 if (opt.count() == 3) {
110 QVariantMap config = ssu.getDomainConfig(ssu.domain());
111 for (QVariantMap::iterator i = config.begin(); i != config.end(); i++) {
112 qout << i.key() <<
": " << i.value().toString() << endl;
115 }
else if (opt.count() == 4) {
116 QVariantMap config = ssu.getDomainConfig(ssu.domain());
117 qout << config.value(opt.at(3)).toString() << endl;
119 }
else if (opt.count() == 5) {
120 QVariantMap config = ssu.getDomainConfig(ssu.domain());
121 config.insert(opt.at(3), opt.at(4));
122 QDBusPendingReply<> reply = ssuProxy->setDomainConfig(ssu.domain(), config);
123 reply.waitForFinished();
124 if (reply.isError()) {
125 qerr << fallingBackToDirectUse(reply.error()) << endl;
126 ssu.setDomainConfig(ssu.domain(), config);
129 }
else if (opt.count() == 3) {
130 if (ssu.listDomains().contains(opt.at(2))) {
131 qout <<
"Changing domain from " << ssu.domain()
132 <<
" to " << opt.at(2) << endl;
136 qout <<
"Domain " << opt.at(2) <<
" does not exist" << endl;
141 }
else if (opt.count() == 2) {
142 qout <<
"Device domain is currently: " << ssu.domain() << endl;
147 void SsuCli::optFlavour(QStringList opt)
149 QTextStream qout(stdout);
150 QTextStream qerr(stderr);
152 if (opt.count() == 3 && opt.at(2) ==
"-s") {
155 }
else if (opt.count() == 3) {
156 qout <<
"Changing flavour from " << ssu.
flavour()
157 <<
" to " << opt.at(2) << endl;
159 QDBusPendingReply<> reply = ssuProxy->setFlavour(opt.at(2));
160 reply.waitForFinished();
161 if (reply.isError()) {
162 qerr << fallingBackToDirectUse(reply.error()) << endl;
171 }
else if (opt.count() == 2) {
172 qout <<
"Device flavour is currently: " << ssu.
flavour() << endl;
177 QString SsuCli::getModeString(
int mode) {
178 QStringList modeList;
181 modeList.append(
"DisableRepoManager");
183 modeList.append(
"RndMode");
185 modeList.append(
"ReleaseMode");
187 modeList.append(
"LenientMode");
189 modeList.append(
"UpdateMode");
191 modeList.append(
"AppInstallMode");
193 return modeList.join(
" | ");
196 void SsuCli::optMode(QStringList opt)
198 QTextStream qout(stdout);
199 QTextStream qerr(stderr);
201 int deviceMode = ssu.deviceMode();
203 if (opt.count() == 2) {
204 qout <<
"Device mode is: " << deviceMode
205 <<
" (" << getModeString(deviceMode) <<
")" << endl;
209 qout <<
"Both Release and RnD mode set, device is in RnD mode" << endl;
212 }
else if (opt.count() == 3 && opt.at(2) ==
"-s") {
216 }
else if (opt.count() >= 3) {
218 int newMode = opt.at(2).toInt(&isInt);
221 for (
int i=2; i<opt.size(); i++) {
222 if (opt.at(i) ==
"DisableRepoManager")
224 else if (opt.at(i) ==
"RndMode")
226 else if (opt.at(i) ==
"ReleaseMode")
228 else if (opt.at(i) ==
"LenientMode")
230 else if (opt.at(i) ==
"UpdateMode")
232 else if (opt.at(i) ==
"AppInstallMode")
235 qout <<
"Unknown mode: " << opt.at(i) << endl;
242 qout <<
"Setting device mode from " << deviceMode
243 <<
" (" << getModeString(deviceMode)
244 <<
") to " << newMode
245 <<
" (" << getModeString(newMode) <<
")" << endl;
247 QDBusPendingReply<> reply = ssuProxy->setDeviceMode(newMode);
248 reply.waitForFinished();
249 if (reply.isError()) {
250 qerr << fallingBackToDirectUse(reply.error()) << endl;
262 void SsuCli::optModel(QStringList opt)
264 QTextStream qout(stdout);
267 if (opt.count() == 3 && opt.at(2) ==
"-s") {
270 }
else if (opt.count() == 2) {
271 qout <<
"Device model is: " << deviceInfo.
deviceModel() << endl;
276 void SsuCli::optModifyRepo(
enum Actions action, QStringList opt)
279 QTextStream qerr(stderr);
281 if (opt.count() == 3) {
282 QDBusPendingReply<> reply = ssuProxy->modifyRepo(action, opt.at(2));
283 reply.waitForFinished();
284 if (reply.isError()) {
285 qerr << fallingBackToDirectUse(reply.error()) << endl;
289 repoManager.
add(opt.at(2));
294 repoManager.
remove(opt.at(2));
299 repoManager.
disable(opt.at(2));
304 repoManager.
enable(opt.at(2));
310 }
else if (opt.count() == 4 && action == Add) {
313 if (opt.at(2).indexOf(QRegExp(
"[a-z]*://", Qt::CaseInsensitive)) == 0) {
316 }
else if (opt.at(3).indexOf(QRegExp(
"[a-z]*://", Qt::CaseInsensitive)) == 0) {
320 qerr <<
"Invalid parameters for 'ssu ar': URL required." << endl;
324 QDBusPendingReply<> reply = ssuProxy->addRepo(repo, url);
325 reply.waitForFinished();
326 if (reply.isError()) {
327 qerr << fallingBackToDirectUse(reply.error()) << endl;
328 repoManager.
add(repo, url);
335 void SsuCli::optRegister(QStringList opt)
341 QString username, password;
342 QTextStream qin(stdin);
343 QTextStream qout(stdout);
344 QTextStream qerr(stderr);
347 struct termios termNew, termOld;
349 qout <<
"Username: " << flush;
350 username = qin.readLine();
352 tcgetattr(STDIN_FILENO, &termNew);
354 termNew.c_lflag &= ~ECHO;
355 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &termNew) == -1)
356 qout <<
"WARNING: Unable to disable echo on your terminal, password will echo!" << endl;
358 qout <<
"Password: " << flush;
359 password = qin.readLine();
362 tcsetattr(STDIN_FILENO, TCSANOW, &termOld);
364 if (opt.count() == 3 && opt.at(2) ==
"-h")
365 ssuSettings->setValue(
"repository-url-variables/user", username);
367 QDBusPendingReply<> reply = ssuProxy->registerDevice(username, password);
368 reply.waitForFinished();
369 if (reply.isError()) {
370 qerr << fallingBackToDirectUse(reply.error()) << endl;
377 void SsuCli::optRelease(QStringList opt)
379 QTextStream qout(stdout);
380 QTextStream qerr(stderr);
382 if (opt.count() == 3) {
383 if (opt.at(2) ==
"-r") {
384 qout <<
"Device release (RnD) is currently: " << ssu.
release(
true) << endl;
387 qout <<
"Changing release from " << ssu.
release()
388 <<
" to " << opt.at(2) << endl;
389 qout <<
"Your device is now in release mode!" << endl;
391 QDBusPendingReply<> reply = ssuProxy->setRelease(opt.at(2),
false);
392 reply.waitForFinished();
393 if (reply.isError()) {
394 qerr << fallingBackToDirectUse(reply.error()) << endl;
404 }
else if (opt.count() == 2) {
405 qout <<
"Device release is currently: " << ssu.
release() << endl;
407 }
else if (opt.count() == 4 && opt.at(2) ==
"-r") {
408 qout <<
"Changing release (RnD) from " << ssu.
release(
true)
409 <<
" to " << opt.at(3) << endl;
410 qout <<
"Your device is now in RnD mode!" << endl;
412 QDBusPendingReply<> reply = ssuProxy->setRelease(opt.at(3),
true);
413 reply.waitForFinished();
414 if (reply.isError()) {
415 qerr << fallingBackToDirectUse(reply.error()) << endl;
427 void SsuCli::optRepos(QStringList opt)
429 QTextStream qout(stdout);
432 QHash<QString, QString> repoParameters, repoOverride;
434 bool rndRepo =
false;
435 int micMode = 0, flagStart = 0;
440 if (opt.count() >= 3 && opt.at(2) ==
"-m") {
451 if (opt.count() >= 3 + micMode) {
453 if (opt.at(2 + micMode).contains(
"=")) {
454 flagStart = 2 + micMode;
455 }
else if (!opt.at(2 + micMode).contains(
"=") &&
456 opt.count() == 3 + micMode) {
458 device = opt.at(2 + micMode);
459 }
else if (!opt.at(2 + micMode).contains(
"=") &&
460 opt.count() > 3 + micMode &&
461 opt.at(3 + micMode).contains(
"=")) {
463 device = opt.at(2 + micMode);
464 flagStart = 3 + micMode;
471 if (flagStart != 0) {
472 for (
int i = flagStart; i < opt.count(); i++) {
473 if (opt.at(i).count(
"=") != 1) {
474 qout <<
"Invalid flag: " << opt.at(i) << endl;
478 QStringList split = opt.at(i).split(
"=");
479 repoOverride.insert(split.at(0), split.at(1));
483 if (repoOverride.contains(
"rnd")) {
484 if (repoOverride.value(
"rnd") ==
"true")
486 else if (repoOverride.value(
"rnd") ==
"false")
490 if (!device.isEmpty()) {
492 repoOverride.insert(
"model", device);
501 foreach (
const QString &repo, repos) {
502 QString repoName = repo;
503 if (repo.endsWith(
"-debuginfo")) {
504 repoName = repo.left(repo.size() - 10);
505 repoParameters.insert(
"debugSplit",
"debug");
506 }
else if (repoParameters.value(
"debugSplit") ==
"debug") {
507 repoParameters.remove(
"debugSplit");
510 QString repoUrl = ssu.
repoUrl(repoName, rndRepo, repoParameters, repoOverride);
511 qout <<
"repo --name=" << repo <<
"-"
512 << (rndRepo ? repoOverride.value(
"rndRelease")
513 : repoOverride.value(
"release"))
514 <<
" --baseurl=" << repoUrl << endl;
520 if (device.isEmpty()) {
523 qout <<
"Printing repository configuration for '" << device <<
"'" << endl << endl;
529 qout <<
"Enabled repositories (global): " << endl;
530 for (
int i = 0; i <= 3; i++) {
532 int longestField = 0;
533 foreach (
const QString &repo, repos)
534 if (repo.length() > longestField)
535 longestField = repo.length();
537 qout.setFieldAlignment(QTextStream::AlignLeft);
539 foreach (
const QString &repo, repos) {
540 QString repoName = repo;
541 if (repo.endsWith(
"-debuginfo")) {
542 repoName = repo.left(repo.size() - 10);
543 repoParameters.insert(
"debugSplit",
"debug");
544 }
else if (repoParameters.value(
"debugSplit") ==
"debug")
545 repoParameters.remove(
"debugSplit");
547 QString repoUrl = ssu.
repoUrl(repoName, rndRepo, repoParameters, repoOverride);
548 qout <<
" - " << qSetFieldWidth(longestField) << repo << qSetFieldWidth(0) <<
" ... " << repoUrl << endl;
552 if (!device.isEmpty()) {
557 qout << endl <<
"Enabled repositories (user): " << endl;
560 if (device.isEmpty())
561 qout << endl <<
"Disabled repositories (global, might be overridden by user config): " << endl;
563 qout << endl <<
"Disabled repositories (global): " << endl;
566 if (!device.isEmpty())
568 if (ssuSettings->contains(
"disabled-repos"))
569 repos.append(ssuSettings->value(
"disabled-repos").toStringList());
570 qout << endl <<
"Disabled repositories (user): " << endl;
577 void SsuCli::optSet(QStringList opt)
579 QTextStream qout(stdout);
582 QHash<QString, QString> storageHash;
585 if (opt.count() == 5 && opt.at(2) ==
"-r") {
586 settings->setValue(
"repository-url-variables/" + opt.at(3), opt.at(4));
588 }
else if (opt.count() == 4 && opt.at(2) ==
"-r") {
589 settings->remove(
"repository-url-variables/" + opt.at(3));
591 }
else if (opt.count() == 3 && opt.at(2) ==
"-r") {
592 qout <<
"Repository specific variables:" << endl << endl;
593 var.
variableSection(settings,
"repository-url-variables", &storageHash);
595 }
else if (opt.count() == 4) {
596 settings->setValue(
"global-variables/" + opt.at(2), opt.at(3));
598 }
else if (opt.count() == 3) {
599 settings->remove(
"global-variables/" + opt.at(2));
601 }
else if (opt.count() == 2) {
602 qout <<
"Global variables:" << endl << endl;
608 if (!storageHash.isEmpty()) {
609 QHash<QString, QString>::const_iterator i = storageHash.constBegin();
610 while (i != storageHash.constEnd()) {
611 qout << i.key() <<
"=" << i.value() << endl;
619 void SsuCli::optStatus(QStringList opt)
623 QTextStream qout(stdout);
624 QTextStream qerr(stderr);
633 QDBusPendingReply<QString> reply = ssuProxy->deviceUid();
634 reply.waitForFinished();
635 if (reply.isError()) {
636 qerr <<
"DBus unavailable, UUID not necessarily connected to reality." << endl;
639 deviceUid = reply.value();
641 qout <<
"Device registration status: "
642 << (ssu.
isRegistered() ?
"registered" :
"not registered") << endl;
647 qout <<
"Device variant: " << deviceInfo.
deviceVariant() << endl;
648 qout <<
"Device UID: " << deviceUid << endl;
650 qout <<
"Release (rnd): " << ssu.
release(
true) <<
" (" << ssu.
flavour() <<
")" << endl;
652 qout <<
"Release: " << ssu.
release() << endl;
653 qout <<
"Domain: " << ssu.domain() << endl;
654 qout <<
"Brand: " << (ssu.
brand().isEmpty() ?
"N/A" : ssu.
brand()) << endl;
657 void SsuCli::optUpdateCredentials(QStringList opt)
659 QTextStream qout(stdout);
665 if (opt.count() == 3 && opt.at(2) ==
"-f")
669 qout <<
"Device is not registered, can't update credentials" << endl;
671 QCoreApplication::exit(1);
678 void SsuCli::optUpdateRepos(QStringList opt)
682 QTextStream qerr(stdout);
684 QDBusPendingReply<> reply = ssuProxy->updateRepos();
685 reply.waitForFinished();
686 if (reply.isError()) {
687 qerr << fallingBackToDirectUse(reply.error()) << endl;
696 QTextStream qerr(stderr);
698 QStringList arguments = QCoreApplication::arguments();
701 if (!ssuSettings->isWritable())
702 qerr <<
"WARNING: ssu.ini does not seem to be writable. Setting values might not work." << endl;
705 if (arguments.count() < 2) {
712 const char *shortopt;
715 void (
SsuCli::*handler)(QStringList opt);
718 "status",
"s", 0, 0, &SsuCli::optStatus,
719 "updaterepos",
"ur", 0, 0, &SsuCli::optUpdateRepos,
722 "addrepo",
"ar", 1, 2, &SsuCli::optAddRepo,
723 "removerepo",
"rr", 1, 1, &SsuCli::optRemoveRepo,
724 "enablerepo",
"er", 1, 1, &SsuCli::optEnableRepo,
725 "disablerepo",
"dr", 1, 1, &SsuCli::optDisableRepo,
729 "register",
"r", 0, -1, &SsuCli::optRegister,
730 "repos",
"lr", 0, -1, &SsuCli::optRepos,
731 "brand",
"b", 0, -1, &SsuCli::optBrand,
732 "flavour",
"fl", 0, -1, &SsuCli::optFlavour,
733 "mode",
"m", 0, -1, &SsuCli::optMode,
734 "model",
"mo", 0, -1, &SsuCli::optModel,
735 "release",
"re", 0, -1, &SsuCli::optRelease,
736 "update",
"up", 0, -1, &SsuCli::optUpdateCredentials,
737 "domain",
"do", 0, -1, &SsuCli::optDomain,
738 "set",
"set", 0, -1, &SsuCli::optSet,
742 int argc = arguments.count() - 2;
744 for (
unsigned int i = 0; i <
sizeof(handlers) /
sizeof(handlers[0]); i++) {
745 if ((arguments.at(1) != handlers[i].longopt) &&
746 (arguments.at(1) != handlers[i].shortopt)) {
750 if (argc < handlers[i].minargs) {
751 usage(QString(
"%1: Too few arguments").arg(handlers[i].longopt));
755 if (handlers[i].maxargs != -1 && argc > handlers[i].maxargs) {
756 usage(QString(
"%1: Too many arguments").arg(handlers[i].longopt));
761 (this->*(handlers[i].handler))(arguments);
773 QCoreApplication::exit(0);
774 else if (state == UserError)
778 void SsuCli::uidWarning()
780 if (geteuid() != 0) {
781 QTextStream qout(stderr);
782 qout <<
"You're not root. Run 'ssu ur' as root to recreate repository files" << endl;
786 void SsuCli::usage(
const QString &message)
788 QTextStream qout(stderr);
789 qout <<
"\nUsage: ssu <command> [-command-options] [arguments]" << endl
791 <<
"Repository management:" << endl
792 <<
"\tmode, m \tShow repository mode information." << endl
793 <<
"\t <int> \tSet repository mode as an integer (see docs)." << endl
794 <<
"\t <modes> \tCombo of modes to set from: DisableRepoManager RndMode " << endl
795 <<
"\t \tReleaseMode LenientMode UpdateMode AppInstallMode" << endl
796 <<
"\tupdaterepos, ur \tupdate repository files" << endl
797 <<
"\trepos, lr \tlist configured repositories" << endl
798 <<
"\t [-m] \tformat output suitable for kickstart" << endl
799 <<
"\t [device] \tuse repos for 'device'" << endl
800 <<
"\t [flags] \tadditional flags" << endl
801 <<
"\t rnd=<bool> \tset rnd or release mode (default: take from host)" << endl
802 <<
"\taddrepo, ar <repo> \tadd this repository" << endl
803 <<
"\t [url] \tspecify URL, if not configured" << endl
804 <<
"\tremoverepo, rr <repo> \tremove this repository from configuration" << endl
805 <<
"\tenablerepo, er <repo> \tenable this repository" << endl
806 <<
"\tdisablerepo, dr <repo> \tdisable this repository" << endl
808 <<
"Configuration management:" << endl
809 <<
"\tflavour, fl \tdisplay flavour used (RnD only)" << endl
810 <<
"\t [newflavour] \tset new flavour" << endl
811 <<
"\trelease, re \tdisplay release used" << endl
812 <<
"\t [-r] \tuse RnD release" << endl
813 <<
"\t [newrelease] \tset new (RnD)release" << endl
814 <<
"\tset \tdisplay global variables" << endl
815 <<
"\t [-r] \toperate on repository only variables" << endl
816 <<
"\t <variable> \tdisplay value of <variable>" << endl
817 <<
"\t <variable> <value> \tset value of <variable> to <value>" << endl
818 <<
"\tdomain do \tdisplay current device domain" << endl
819 <<
"\t [newdomain] \tset new domain" << endl
820 <<
"\t [-c] \tshow domain configuration" << endl
821 <<
"\t -c <variable> \tshow single domain variable" << endl
822 <<
"\t -c <variable> <val>\tset single domain variable" << endl
824 <<
"Device management:" << endl
825 <<
"\tstatus, s \tprint registration status and device information" << endl
826 <<
"\tregister, r \tregister this device" << endl
827 <<
"\t [-h] \tconfigure user for OBS home" << endl
828 <<
"\tupdate, up \tupdate repository credentials" << endl
829 <<
"\t [-f] \tforce update" << endl
830 <<
"\tmodel, mo \tprint name of device model (like N9)" << endl
831 <<
"\tbrand, b \tprint brand of device model" << endl
833 if (!message.isEmpty())
834 qout << message << endl;
836 QCoreApplication::exit(1);
839 QString SsuCli::fallingBackToDirectUse(
const QDBusError &reason)
const
841 if (reason.type() == QDBusError::Disconnected)
842 return QStringLiteral(
"DBus unavailable, falling back to libssu");
844 return QStringLiteral(
"WARNING: DBus call failed, falling back to libssu: ") + reason.message();