ssu
ssucli.cpp
Go to the documentation of this file.
1 
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 
29 #include "libssu/ssudeviceinfo.h"
30 #include "libssu/ssurepomanager.h"
31 #include "libssu/ssucoreconfig_p.h"
32 #include "libssu/ssuvariables_p.h"
33 
34 #include <QDebug>
35 
36 #include "ssucli.h"
37 
38 SsuCli::SsuCli()
39  : QObject()
40 {
41  qDBusRegisterMetaType<SsuRepo>();
42 
43  connect(this, SIGNAL(done()),
44  QCoreApplication::instance(), SLOT(quit()), Qt::DirectConnection);
45  connect(&ssu, SIGNAL(done()),
46  this, SLOT(handleResponse()));
47 
48  ssuProxy = new SsuProxy();
49 
50  connect(ssuProxy, SIGNAL(done()),
51  this, SLOT(handleDBusResponse()));
52 
53  state = Idle;
54 }
55 
56 SsuCli::~SsuCli()
57 {
58  ssuProxy->quit();
59 }
60 
61 void SsuCli::handleResponse()
62 {
63  QTextStream qout(stdout);
64 
65  if (ssu.error()) {
66  qout << "Last operation failed: \n" << ssu.lastError() << endl;
67  QCoreApplication::exit(1);
68  } else {
69  qout << "Operation successful (direct)" << endl;
70  QCoreApplication::exit(0);
71  }
72 }
73 
74 void SsuCli::handleDBusResponse()
75 {
76  QTextStream qout(stdout);
77 
78  if (ssuProxy->error()) {
79  qout << "Last operation failed: \n" << ssuProxy->lastError() << endl;
80  QCoreApplication::exit(1);
81  } else {
82  qout << "Operation successful" << endl;
83  QCoreApplication::exit(0);
84  }
85 }
86 
87 void SsuCli::optBrand(QStringList opt)
88 {
89  QTextStream qout(stdout);
90 
91  if (opt.count() == 3 && opt.at(2) == "-s") {
92  qout << ssu.brand();
93  state = Idle;
94  } else if (opt.count() == 2) {
95  qout << "Device brand is: " << ssu.brand() << endl;
96  state = Idle;
97  }
98 }
99 
100 void SsuCli::optDomain(QStringList opt)
101 {
102  QTextStream qout(stdout);
103  QTextStream qerr(stderr);
104 
105  if (opt.count() == 3 && opt.at(2) == "-s") {
106  qout << ssu.domain();
107  state = Idle;
108  } else if (opt.count() > 2 && opt.at(2) == "-c") {
109  if (opt.count() == 3) { // dump all domain config
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;
113  }
114  state = Idle;
115  } else if (opt.count() == 4) { // dump one domain config value
116  QVariantMap config = ssu.getDomainConfig(ssu.domain());
117  qout << config.value(opt.at(3)).toString() << endl;
118  state = Idle;
119  } else if (opt.count() == 5) { // set one domain config value
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);
127  }
128  }
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;
133  ssu.setDomain(opt.at(2));
134  }
135  else {
136  qout << "Domain " << opt.at(2) << " does not exist" << endl;
137  state = Idle;
138  }
139 
140  state = Idle;
141  } else if (opt.count() == 2) {
142  qout << "Device domain is currently: " << ssu.domain() << endl;
143  state = Idle;
144  }
145 }
146 
147 void SsuCli::optFlavour(QStringList opt)
148 {
149  QTextStream qout(stdout);
150  QTextStream qerr(stderr);
151 
152  if (opt.count() == 3 && opt.at(2) == "-s") {
153  qout << ssu.flavour();
154  state = Idle;
155  } else if (opt.count() == 3) {
156  qout << "Changing flavour from " << ssu.flavour()
157  << " to " << opt.at(2) << endl;
158 
159  QDBusPendingReply<> reply = ssuProxy->setFlavour(opt.at(2));
160  reply.waitForFinished();
161  if (reply.isError()) {
162  qerr << fallingBackToDirectUse(reply.error()) << endl;
163  ssu.setFlavour(opt.at(2));
164 
165  SsuRepoManager repoManager;
166  repoManager.update();
167  uidWarning();
168  }
169 
170  state = Idle;
171  } else if (opt.count() == 2) {
172  qout << "Device flavour is currently: " << ssu.flavour() << endl;
173  state = Idle;
174  }
175 }
176 
177 QString SsuCli::getModeString(int mode) {
178  QStringList modeList;
179 
181  modeList.append("DisableRepoManager");
182  if ((mode & Ssu::RndMode) == Ssu::RndMode)
183  modeList.append("RndMode");
184  if ((mode & Ssu::ReleaseMode) == Ssu::ReleaseMode)
185  modeList.append("ReleaseMode");
186  if ((mode & Ssu::LenientMode) == Ssu::LenientMode)
187  modeList.append("LenientMode");
188  if ((mode & Ssu::UpdateMode) == Ssu::UpdateMode)
189  modeList.append("UpdateMode");
191  modeList.append("AppInstallMode");
192 
193  return modeList.join(" | ");
194 }
195 
196 void SsuCli::optMode(QStringList opt)
197 {
198  QTextStream qout(stdout);
199  QTextStream qerr(stderr);
200 
201  int deviceMode = ssu.deviceMode();
202 
203  if (opt.count() == 2) {
204  qout << "Device mode is: " << deviceMode
205  << " (" << getModeString(deviceMode) << ")" << endl;
206 
207  if ((deviceMode & Ssu::RndMode) == Ssu::RndMode &&
208  (deviceMode & Ssu::ReleaseMode) == Ssu::ReleaseMode)
209  qout << "Both Release and RnD mode set, device is in RnD mode" << endl;
210 
211  state = Idle;
212  } else if (opt.count() == 3 && opt.at(2) == "-s") {
213  qout << deviceMode;
214  state = Idle;
215  return;
216  } else if (opt.count() >= 3) {
217  bool isInt;
218  int newMode = opt.at(2).toInt(&isInt);
219 
220  if (!isInt) {
221  for (int i=2; i<opt.size(); i++) {
222  if (opt.at(i) == "DisableRepoManager")
223  newMode |= Ssu::DisableRepoManager;
224  else if (opt.at(i) == "RndMode")
225  newMode |= Ssu::RndMode;
226  else if (opt.at(i) == "ReleaseMode")
227  newMode |= Ssu::ReleaseMode;
228  else if (opt.at(i) == "LenientMode")
229  newMode |= Ssu::LenientMode;
230  else if (opt.at(i) == "UpdateMode")
231  newMode |= Ssu::UpdateMode;
232  else if (opt.at(i) == "AppInstallMode")
233  newMode |= Ssu::AppInstallMode;
234  else {
235  qout << "Unknown mode: " << opt.at(i) << endl;
236  state = Idle;
237  return;
238  }
239  }
240  }
241 
242  qout << "Setting device mode from " << deviceMode
243  << " (" << getModeString(deviceMode)
244  << ") to " << newMode
245  << " (" << getModeString(newMode) << ")" << endl;
246 
247  QDBusPendingReply<> reply = ssuProxy->setDeviceMode(newMode);
248  reply.waitForFinished();
249  if (reply.isError()) {
250  qerr << fallingBackToDirectUse(reply.error()) << endl;
251  ssu.setDeviceMode(Ssu::DeviceModeFlags(newMode));
252 
253  SsuRepoManager repoManager;
254  repoManager.update();
255  uidWarning();
256  }
257  }
258 
259  state = Idle;
260 }
261 
262 void SsuCli::optModel(QStringList opt)
263 {
264  QTextStream qout(stdout);
265  SsuDeviceInfo deviceInfo;
266 
267  if (opt.count() == 3 && opt.at(2) == "-s") {
268  qout << deviceInfo.deviceModel();
269  state = Idle;
270  } else if (opt.count() == 2) {
271  qout << "Device model is: " << deviceInfo.deviceModel() << endl;
272  state = Idle;
273  }
274 }
275 
276 void SsuCli::optModifyRepo(enum Actions action, QStringList opt)
277 {
278  SsuRepoManager repoManager;
279  QTextStream qerr(stderr);
280 
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;
286 
287  switch (action) {
288  case Add:
289  repoManager.add(opt.at(2));
290  repoManager.update();
291  uidWarning();
292  break;
293  case Remove:
294  repoManager.remove(opt.at(2));
295  repoManager.update();
296  uidWarning();
297  break;
298  case Disable:
299  repoManager.disable(opt.at(2));
300  repoManager.update();
301  uidWarning();
302  break;
303  case Enable:
304  repoManager.enable(opt.at(2));
305  repoManager.update();
306  uidWarning();
307  break;
308  }
309  }
310  } else if (opt.count() == 4 && action == Add) {
311  QString url, repo;
312 
313  if (opt.at(2).indexOf(QRegExp("[a-z]*://", Qt::CaseInsensitive)) == 0) {
314  url = opt.at(2);
315  repo = opt.at(3);
316  } else if (opt.at(3).indexOf(QRegExp("[a-z]*://", Qt::CaseInsensitive)) == 0) {
317  url = opt.at(3);
318  repo = opt.at(2);
319  } else {
320  qerr << "Invalid parameters for 'ssu ar': URL required." << endl;
321  return;
322  }
323 
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);
329  repoManager.update();
330  uidWarning();
331  }
332  }
333 }
334 
335 void SsuCli::optRegister(QStringList opt)
336 {
337  /*
338  * register a new device
339  */
340 
341  QString username, password;
342  QTextStream qin(stdin);
343  QTextStream qout(stdout);
344  QTextStream qerr(stderr);
345  SsuCoreConfig *ssuSettings = SsuCoreConfig::instance();
346 
347  struct termios termNew, termOld;
348 
349  qout << "Username: " << flush;
350  username = qin.readLine();
351 
352  tcgetattr(STDIN_FILENO, &termNew);
353  termOld = 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;
357 
358  qout << "Password: " << flush;
359  password = qin.readLine();
360  qout << endl;
361 
362  tcsetattr(STDIN_FILENO, TCSANOW, &termOld);
363 
364  if (opt.count() == 3 && opt.at(2) == "-h")
365  ssuSettings->setValue("repository-url-variables/user", username);
366 
367  QDBusPendingReply<> reply = ssuProxy->registerDevice(username, password);
368  reply.waitForFinished();
369  if (reply.isError()) {
370  qerr << fallingBackToDirectUse(reply.error()) << endl;
371  ssu.sendRegistration(username, password);
372  }
373 
374  state = Busy;
375 }
376 
377 void SsuCli::optRelease(QStringList opt)
378 {
379  QTextStream qout(stdout);
380  QTextStream qerr(stderr);
381 
382  if (opt.count() == 3) {
383  if (opt.at(2) == "-r") {
384  qout << "Device release (RnD) is currently: " << ssu.release(true) << endl;
385  state = Idle;
386  } else {
387  qout << "Changing release from " << ssu.release()
388  << " to " << opt.at(2) << endl;
389  qout << "Your device is now in release mode!" << endl;
390 
391  QDBusPendingReply<> reply = ssuProxy->setRelease(opt.at(2), false);
392  reply.waitForFinished();
393  if (reply.isError()) {
394  qerr << fallingBackToDirectUse(reply.error()) << endl;
395  ssu.setRelease(opt.at(2));
396 
397  SsuRepoManager repoManager;
398  repoManager.update();
399  uidWarning();
400  }
401 
402  state = Idle;
403  }
404  } else if (opt.count() == 2) {
405  qout << "Device release is currently: " << ssu.release() << endl;
406  state = Idle;
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;
411 
412  QDBusPendingReply<> reply = ssuProxy->setRelease(opt.at(3), true);
413  reply.waitForFinished();
414  if (reply.isError()) {
415  qerr << fallingBackToDirectUse(reply.error()) << endl;
416  ssu.setRelease(opt.at(3), true);
417 
418  SsuRepoManager repoManager;
419  repoManager.update();
420  uidWarning();
421  }
422 
423  state = Idle;
424  }
425 }
426 
427 void SsuCli::optRepos(QStringList opt)
428 {
429  QTextStream qout(stdout);
430  SsuRepoManager repoManager;
431  SsuDeviceInfo deviceInfo;
432  QHash<QString, QString> repoParameters, repoOverride;
433  QString device;
434  bool rndRepo = false;
435  int micMode = 0, flagStart = 0;
436 
437  if ((ssu.deviceMode() & Ssu::RndMode) == Ssu::RndMode)
438  rndRepo = true;
439 
440  if (opt.count() >= 3 && opt.at(2) == "-m") {
441  micMode = 1;
442  // TODO: read the default mic override variables from some config
443  /*
444  repoOverride.insert("release", "@RELEASE@");
445  repoOverride.insert("rndRelease", "@RNDRELEASE@");
446  repoOverride.insert("flavour", "@FLAVOUR@");
447  repoOverride.insert("arch", "@ARCH@");
448  */
449  }
450 
451  if (opt.count() >= 3 + micMode) {
452  // first argument is flag
453  if (opt.at(2 + micMode).contains("=")) {
454  flagStart = 2 + micMode;
455  } else if (!opt.at(2 + micMode).contains("=") &&
456  opt.count() == 3 + micMode) {
457  // first (and only) argument is device)
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("=")) {
462  // first argument is device, second flag
463  device = opt.at(2 + micMode);
464  flagStart = 3 + micMode;
465  } else {
466  state = UserError;
467  return;
468  }
469  }
470 
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;
475  state = Idle;
476  return;
477  }
478  QStringList split = opt.at(i).split("=");
479  repoOverride.insert(split.at(0), split.at(1));
480  }
481  }
482 
483  if (repoOverride.contains("rnd")) {
484  if (repoOverride.value("rnd") == "true")
485  rndRepo = true;
486  else if (repoOverride.value("rnd") == "false")
487  rndRepo = false;
488  }
489 
490  if (!device.isEmpty()) {
491  deviceInfo.setDeviceModel(device);
492  repoOverride.insert("model", device);
493  }
494 
495  // TODO: rnd mode override needs to be implemented
496  QStringList repos;
497 
498  // micMode? handle it and return, as it's a lot simpler than full mode
499  if (micMode) {
500  repos = repoManager.repos(rndRepo, deviceInfo, Ssu::BoardFilter);
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");
508  }
509 
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;
515  }
516  state = Idle;
517  return;
518  }
519 
520  if (device.isEmpty()) {
521  repos = repoManager.repos(rndRepo, deviceInfo, Ssu::BoardFilterUserBlacklist);
522  } else {
523  qout << "Printing repository configuration for '" << device << "'" << endl << endl;
524  repos = repoManager.repos(rndRepo, deviceInfo, Ssu::BoardFilter);
525  }
526 
527  SsuCoreConfig *ssuSettings = SsuCoreConfig::instance();
528 
529  qout << "Enabled repositories (global): " << endl;
530  for (int i = 0; i <= 3; i++) {
531  // for each repository, print repo and resolve url
532  int longestField = 0;
533  foreach (const QString &repo, repos)
534  if (repo.length() > longestField)
535  longestField = repo.length();
536 
537  qout.setFieldAlignment(QTextStream::AlignLeft);
538 
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");
546 
547  QString repoUrl = ssu.repoUrl(repoName, rndRepo, repoParameters, repoOverride);
548  qout << " - " << qSetFieldWidth(longestField) << repo << qSetFieldWidth(0) << " ... " << repoUrl << endl;
549  }
550 
551  if (i == 0) {
552  if (!device.isEmpty()) {
553  repos.clear();
554  continue;
555  }
556  repos = repoManager.repos(rndRepo, deviceInfo, Ssu::UserFilter);
557  qout << endl << "Enabled repositories (user): " << endl;
558  } else if (i == 1) {
559  repos = deviceInfo.disabledRepos();
560  if (device.isEmpty())
561  qout << endl << "Disabled repositories (global, might be overridden by user config): " << endl;
562  else
563  qout << endl << "Disabled repositories (global): " << endl;
564  } else if (i == 2) {
565  repos.clear();
566  if (!device.isEmpty())
567  continue;
568  if (ssuSettings->contains("disabled-repos"))
569  repos.append(ssuSettings->value("disabled-repos").toStringList());
570  qout << endl << "Disabled repositories (user): " << endl;
571  }
572  }
573 
574  state = Idle;
575 }
576 
577 void SsuCli::optSet(QStringList opt)
578 {
579  QTextStream qout(stdout);
580  SsuVariables var;
581  SsuCoreConfig *settings = SsuCoreConfig::instance();
582  QHash<QString, QString> storageHash;
583 
584  // set repository specific variable
585  if (opt.count() == 5 && opt.at(2) == "-r") {
586  settings->setValue("repository-url-variables/" + opt.at(3), opt.at(4));
587  // clear repo specific variable
588  } else if (opt.count() == 4 && opt.at(2) == "-r") {
589  settings->remove("repository-url-variables/" + opt.at(3));
590  // list repo specific variables
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);
594  // set global variable
595  } else if (opt.count() == 4) {
596  settings->setValue("global-variables/" + opt.at(2), opt.at(3));
597  // clear global variable
598  } else if (opt.count() == 3) {
599  settings->remove("global-variables/" + opt.at(2));
600  // list global variables
601  } else if (opt.count() == 2) {
602  qout << "Global variables:" << endl << endl;
603  var.variableSection(settings, "global-variables", &storageHash);
604  }
605 
606  settings->sync();
607 
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;
612  i++;
613  }
614  }
615 
616  state = Idle;
617 }
618 
619 void SsuCli::optStatus(QStringList opt)
620 {
621  Q_UNUSED(opt)
622 
623  QTextStream qout(stdout);
624  QTextStream qerr(stderr);
625  SsuDeviceInfo deviceInfo;
626 
627  /*
628  * print device information and registration status
629  */
630 
631  QString deviceUid;
632 
633  QDBusPendingReply<QString> reply = ssuProxy->deviceUid();
634  reply.waitForFinished();
635  if (reply.isError()) {
636  qerr << "DBus unavailable, UUID not necessarily connected to reality." << endl;
637  deviceUid = deviceInfo.deviceUid();
638  } else
639  deviceUid = reply.value();
640 
641  qout << "Device registration status: "
642  << (ssu.isRegistered() ? "registered" : "not registered") << endl;
643  qout << "Device model: " << deviceInfo.displayName(Ssu::DeviceModel) << " ("
644  << deviceInfo.deviceModel() << " / "
645  << deviceInfo.displayName(Ssu::DeviceDesignation) << ")" << endl;
646  if (!deviceInfo.deviceVariant().isEmpty())
647  qout << "Device variant: " << deviceInfo.deviceVariant() << endl;
648  qout << "Device UID: " << deviceUid << endl;
649  if ((ssu.deviceMode() & Ssu::RndMode) == Ssu::RndMode)
650  qout << "Release (rnd): " << ssu.release(true) << " (" << ssu.flavour() << ")" << endl;
651  else
652  qout << "Release: " << ssu.release() << endl;
653  qout << "Domain: " << ssu.domain() << endl;
654  qout << "Brand: " << (ssu.brand().isEmpty() ? "N/A" : ssu.brand()) << endl;
655 }
656 
657 void SsuCli::optUpdateCredentials(QStringList opt)
658 {
659  QTextStream qout(stdout);
660  /*
661  * update the credentials
662  * optional argument: -f
663  */
664  bool force = false;
665  if (opt.count() == 3 && opt.at(2) == "-f")
666  force = true;
667 
668  if (!ssu.isRegistered()) {
669  qout << "Device is not registered, can't update credentials" << endl;
670  state = Idle;
671  QCoreApplication::exit(1);
672  } else {
673  ssu.updateCredentials(force);
674  state = Busy;
675  }
676 }
677 
678 void SsuCli::optUpdateRepos(QStringList opt)
679 {
680  Q_UNUSED(opt)
681 
682  QTextStream qerr(stdout);
683 
684  QDBusPendingReply<> reply = ssuProxy->updateRepos();
685  reply.waitForFinished();
686  if (reply.isError()) {
687  qerr << fallingBackToDirectUse(reply.error()) << endl;
688  SsuRepoManager repoManager;
689  repoManager.update();
690  uidWarning();
691  }
692 }
693 
694 void SsuCli::run()
695 {
696  QTextStream qerr(stderr);
697 
698  QStringList arguments = QCoreApplication::arguments();
699 
700  SsuCoreConfig *ssuSettings = SsuCoreConfig::instance();
701  if (!ssuSettings->isWritable())
702  qerr << "WARNING: ssu.ini does not seem to be writable. Setting values might not work." << endl;
703 
704  // make sure there's a first argument to parse
705  if (arguments.count() < 2) {
706  usage();
707  return;
708  }
709 
710  struct {
711  const char *longopt; // long option name
712  const char *shortopt; // option shortcut name
713  int minargs; // minimum number of required args
714  int maxargs; // -1 for "function will handle max args"
715  void (SsuCli::*handler)(QStringList opt); // handler function
716  } handlers[] = {
717  // functions accepting no additional arguments
718  "status", "s", 0, 0, &SsuCli::optStatus,
719  "updaterepos", "ur", 0, 0, &SsuCli::optUpdateRepos,
720 
721  // functions requiring at least one argument
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,
726 
727  // functions accepting 0 or more arguments
728  // those need to set state to Idle on success
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,
739  };
740 
741  bool found = false;
742  int argc = arguments.count() - 2;
743 
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)) {
747  continue;
748  }
749 
750  if (argc < handlers[i].minargs) {
751  usage(QString("%1: Too few arguments").arg(handlers[i].longopt));
752  return;
753  }
754 
755  if (handlers[i].maxargs != -1 && argc > handlers[i].maxargs) {
756  usage(QString("%1: Too many arguments").arg(handlers[i].longopt));
757  return;
758  }
759 
760  // Call option handler
761  (this->*(handlers[i].handler))(arguments);
762 
763  found = true;
764  break;
765  }
766 
767  if (!found)
768  state = UserError;
769 
770  // functions that need to wait for a response from ssu should set a flag so
771  // we can do default exit catchall here
772  if (state == Idle)
773  QCoreApplication::exit(0);
774  else if (state == UserError)
775  usage();
776 }
777 
778 void SsuCli::uidWarning()
779 {
780  if (geteuid() != 0) {
781  QTextStream qout(stderr);
782  qout << "You're not root. Run 'ssu ur' as root to recreate repository files" << endl;
783  }
784 }
785 
786 void SsuCli::usage(const QString &message)
787 {
788  QTextStream qout(stderr);
789  qout << "\nUsage: ssu <command> [-command-options] [arguments]" << endl
790  << 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
807  << 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
823  << 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
832  << endl;
833  if (!message.isEmpty())
834  qout << message << endl;
835  qout.flush();
836  QCoreApplication::exit(1);
837 }
838 
839 QString SsuCli::fallingBackToDirectUse(const QDBusError &reason) const
840 {
841  if (reason.type() == QDBusError::Disconnected)
842  return QStringLiteral("DBus unavailable, falling back to libssu");
843  else
844  return QStringLiteral("WARNING: DBus call failed, falling back to libssu: ") + reason.message();
845 }
Ssu::updateCredentials
void updateCredentials(bool force=false)
Definition: ssu.cpp:628
SsuRepoManager::enable
int enable(const QString &repo)
Definition: ssurepomanager.cpp:113
Ssu::flavour
Q_INVOKABLE QString flavour()
See SsuCoreConfig::flavour.
Definition: ssu.cpp:156
Ssu::ReleaseMode
@ ReleaseMode
Enable Release mode.
Definition: ssu.h:69
SsuVariables::variableSection
void variableSection(const QString &section, QHash< QString, QString > *storageHash)
Definition: ssuvariables.cpp:173
Ssu::brand
Q_INVOKABLE QString brand()
See SsuCoreConfig::brand.
Definition: ssu.cpp:174
Ssu::sendRegistration
void sendRegistration(const QString &username, const QString &password)
Definition: ssu.cpp:409
Ssu::setFlavour
Q_INVOKABLE void setFlavour(const QString &flavour)
See SsuCoreConfig::setFlavour.
Definition: ssu.cpp:203
SsuProxy
Definition: ssuproxy.h:30
SsuDeviceInfo::deviceUid
Q_INVOKABLE QString deviceUid()
Definition: ssudeviceinfo.cpp:320
Ssu::setDeviceMode
Q_INVOKABLE void setDeviceMode(DeviceModeFlags mode, enum EditMode editMode=Replace)
See SsuCoreConfig::setDeviceMode.
Definition: ssu.cpp:197
ssuvariables_p.h
Ssu::RndMode
@ RndMode
Enable RnD mode for device.
Definition: ssu.h:68
SsuRepoManager
Definition: ssurepomanager.h:18
SsuRepoManager::add
int add(const QString &repo, const QString &repoUrl=QString())
Definition: ssurepomanager.cpp:49
Ssu::UpdateMode
@ UpdateMode
Do repo isolation and similar bits important for updating devices.
Definition: ssu.h:71
Ssu::isRegistered
Q_INVOKABLE bool isRegistered()
See SsuCoreConfig::isRegistered.
Definition: ssu.cpp:179
SsuDeviceInfo::displayName
Q_INVOKABLE QString displayName(int type)
Definition: ssudeviceinfo.cpp:366
Ssu::DisableRepoManager
@ DisableRepoManager
Disable automagic repository management.
Definition: ssu.h:67
Ssu::setDomain
Q_INVOKABLE void setDomain(const QString &domain)
See SsuCoreConfig::setDomain.
Definition: ssu.cpp:215
ssurepomanager.h
SsuRepoManager::remove
int remove(const QString &repo)
Definition: ssurepomanager.cpp:136
ssucoreconfig_p.h
Ssu::repoUrl
QString repoUrl(const QString &repoName, bool rndRepo=false, QHash< QString, QString > repoParameters=QHash< QString, QString >(), QHash< QString, QString > parametersOverride=QHash< QString, QString >())
Definition: ssu.cpp:312
SsuRepoManager::disable
int disable(const QString &repo)
Definition: ssurepomanager.cpp:96
SsuCoreConfig
Definition: ssucoreconfig_p.h:27
SsuRepoManager::repos
QStringList repos(int filter=Ssu::NoFilter)
Definition: ssurepomanager.cpp:169
Ssu::LenientMode
@ LenientMode
Disable strict mode (i.e., keep unmanaged repositories)
Definition: ssu.h:70
Ssu::lastError
Q_INVOKABLE QString lastError()
Definition: ssu.cpp:231
Ssu::release
Q_INVOKABLE QString release(bool rnd=false)
See SsuCoreConfig::release.
Definition: ssu.cpp:191
SsuDeviceInfo::setDeviceModel
Q_INVOKABLE void setDeviceModel(const QString &model=QString())
Definition: ssudeviceinfo.cpp:475
SsuDeviceInfo::disabledRepos
QStringList disabledRepos()
Definition: ssudeviceinfo.cpp:354
SsuDeviceInfo
Definition: ssudeviceinfo.h:17
SsuCli
Definition: ssucli.h:36
ssucli.h
Ssu::DeviceModel
@ DeviceModel
Marketed device name, like Pogoblaster 3000. Board mappings key "prettyModel".
Definition: ssu.h:83
Ssu::AppInstallMode
@ AppInstallMode
Do repo isolation, but keep store repository enabled.
Definition: ssu.h:72
ssudeviceinfo.h
Ssu::error
Q_INVOKABLE bool error()
Definition: ssu.cpp:150
SsuRepoManager::update
void update()
Definition: ssurepomanager.cpp:268
Ssu::BoardFilter
@ BoardFilter
Only global repositories, with user blacklist ignored.
Definition: ssu.h:52
SsuVariables
Definition: ssuvariables_p.h:16
SsuDeviceInfo::deviceModel
Q_INVOKABLE QString deviceModel()
Definition: ssudeviceinfo.cpp:185
Ssu::setRelease
Q_INVOKABLE void setRelease(const QString &release, bool rnd=false)
See SsuCoreConfig::setRelease.
Definition: ssu.cpp:209
Ssu::BoardFilterUserBlacklist
@ BoardFilterUserBlacklist
Only global repositories, with user blacklist applied.
Definition: ssu.h:53
SsuDeviceInfo::deviceVariant
Q_INVOKABLE QString deviceVariant(bool fallback=false)
Definition: ssudeviceinfo.cpp:170
Ssu::DeviceDesignation
@ DeviceDesignation
Type designation, like NCC-1701. Beard mappings key "deviceDesignation".
Definition: ssu.h:84
Ssu::UserFilter
@ UserFilter
Only user configured repositories.
Definition: ssu.h:51