/
rndssucli.cpp
553 lines (476 loc) · 16.8 KB
1
2
3
4
5
6
7
8
/**
* @file rndssucli.cpp
* @copyright 2012 Jolla Ltd.
* @author Bernd Wachter <bernd.wachter@jollamobile.com>
* @date 2012
*/
#include <QCoreApplication>
9
10
#include <termios.h>
11
#include <unistd.h>
12
13
14
15
#include "libssu/ssudeviceinfo.h"
#include "libssu/ssurepomanager.h"
#include "libssu/ssucoreconfig.h"
16
17
#include <QDebug>
18
19
20
21
22
23
24
25
#include "rndssucli.h"
RndSsuCli::RndSsuCli(): QObject(){
connect(this,SIGNAL(done()),
QCoreApplication::instance(),SLOT(quit()), Qt::DirectConnection);
connect(&ssu, SIGNAL(done()),
this, SLOT(handleResponse()));
26
state = Idle;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
}
void RndSsuCli::handleResponse(){
QTextStream qout(stdout);
if (ssu.error()){
qout << "Last operation failed: \n" << ssu.lastError() << endl;
QCoreApplication::exit(1);
} else {
qout << "Operation successful" << endl;
QCoreApplication::exit(0);
}
}
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
void RndSsuCli::optDomain(QStringList opt){
QTextStream qout(stdout);
if (opt.count() == 3 && opt.at(2) == "-s"){
qout << ssu.domain();
state = Idle;
} else if (opt.count() == 3){
qout << "Changing domain from " << ssu.domain()
<< " to " << opt.at(2) << endl;
ssu.setDomain(opt.at(2));
state = Idle;
} else if (opt.count() == 2) {
qout << "Device domain is currently: " << ssu.domain() << endl;
state = Idle;
}
}
58
59
void RndSsuCli::optFlavour(QStringList opt){
60
61
QTextStream qout(stdout);
62
63
64
65
if (opt.count() == 3 && opt.at(2) == "-s"){
qout << ssu.flavour();
state = Idle;
} else if (opt.count() == 3){
66
qout << "Changing flavour from " << ssu.flavour()
67
68
<< " to " << opt.at(2) << endl;
ssu.setFlavour(opt.at(2));
69
70
71
72
73
SsuRepoManager repoManager;
repoManager.update();
uidWarning();
74
75
state = Idle;
} else if (opt.count() == 2) {
76
qout << "Device flavour is currently: " << ssu.flavour() << endl;
77
78
79
state = Idle;
}
}
80
81
82
void RndSsuCli::optMode(QStringList opt){
QTextStream qout(stdout);
83
84
85
86
// TODO: allow setting meaningful names instead of numbers
if (opt.count() == 2){
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
QStringList modeList;
int deviceMode = ssu.deviceMode();
if ((deviceMode & Ssu::DisableRepoManager) == Ssu::DisableRepoManager)
modeList.append("DisableRepoManager");
if ((deviceMode & Ssu::RndMode) == Ssu::RndMode)
modeList.append("RndMode");
if ((deviceMode & Ssu::ReleaseMode) == Ssu::ReleaseMode)
modeList.append("ReleaseMode");
if ((deviceMode & Ssu::LenientMode) == Ssu::LenientMode)
modeList.append("LenientMode");
qout << "Device mode is: " << ssu.deviceMode()
<< " (" << modeList.join(" | ") << ")" << endl;
if ((deviceMode & Ssu::RndMode) == Ssu::RndMode &&
(deviceMode & Ssu::ReleaseMode) == Ssu::ReleaseMode)
qout << "Both Release and RnD mode set, device is in RnD mode" << endl;
106
107
108
state = Idle;
} else if (opt.count() == 3 && opt.at(2) == "-s"){
qout << ssu.deviceMode();
109
110
111
112
113
state = Idle;
} else if (opt.count() == 3){
qout << "Setting device mode from " << ssu.deviceMode()
<< " to " << opt.at(2) << endl;
ssu.setDeviceMode(opt.at(2).toInt());
114
115
116
117
118
SsuRepoManager repoManager;
repoManager.update();
uidWarning();
119
120
state = Idle;
}
121
122
}
123
124
125
126
127
128
129
130
131
132
133
134
135
void RndSsuCli::optModel(QStringList opt){
QTextStream qout(stdout);
SsuDeviceInfo deviceInfo;
if (opt.count() == 3 && opt.at(2) == "-s"){
qout << deviceInfo.deviceModel();
state = Idle;
} else if (opt.count() == 2){
qout << "Device model is: " << deviceInfo.deviceModel() << endl;
state = Idle;
}
}
136
137
138
void RndSsuCli::optModifyRepo(int action, QStringList opt){
SsuRepoManager repoManager;
QTextStream qout(stdout);
139
QTextStream qerr(stderr);
140
141
if (opt.count() == 3){
142
143
144
145
switch(action){
case Add:
repoManager.add(opt.at(2));
repoManager.update();
146
uidWarning();
147
148
149
150
break;
case Remove:
repoManager.remove(opt.at(2));
repoManager.update();
151
uidWarning();
152
153
154
155
break;
case Disable:
repoManager.disable(opt.at(2));
repoManager.update();
156
uidWarning();
157
158
159
160
break;
case Enable:
repoManager.enable(opt.at(2));
repoManager.update();
161
uidWarning();
162
163
164
break;
}
} else if (opt.count() == 4 && action == Add){
165
166
167
168
169
170
171
172
173
174
175
176
177
178
QString url, repo;
if (opt.at(2).indexOf(QRegExp("[a-z]*://", Qt::CaseInsensitive)) == 0){
url = opt.at(2);
repo = opt.at(3);
} else if (opt.at(3).indexOf(QRegExp("[a-z]*://", Qt::CaseInsensitive)) == 0){
url = opt.at(3);
repo = opt.at(2);
} else {
qerr << "Invalid parameters for 'ssu ar': URL required." << endl;
return;
}
repoManager.add(repo, url);
179
repoManager.update();
180
uidWarning();
181
}
182
183
}
184
void RndSsuCli::optRegister(QStringList opt){
185
186
187
188
189
190
191
/*
* register a new device
*/
QString username, password;
QTextStream qin(stdin);
QTextStream qout(stdout);
192
SsuCoreConfig *ssuSettings = SsuCoreConfig::instance();
193
194
195
struct termios termNew, termOld;
196
197
qout << "Username: " << flush;
username = qin.readLine();
198
199
200
201
202
tcgetattr(STDIN_FILENO, &termNew);
termOld = termNew;
termNew.c_lflag &= ~ECHO;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &termNew) == -1)
203
qout << "WARNING: Unable to disable echo on your terminal, password will echo!" << endl;
204
205
206
207
qout << "Password: " << flush;
password = qin.readLine();
208
209
tcsetattr(STDIN_FILENO, TCSANOW, &termOld);
210
211
212
if (opt.count() == 3 && opt.at(2) == "-h")
ssuSettings->setValue("repository-url-variables/user", username);
213
ssu.sendRegistration(username, password);
214
215
216
217
218
219
220
221
222
223
224
225
226
state = Busy;
}
void RndSsuCli::optRelease(QStringList opt){
QTextStream qout(stdout);
if (opt.count() == 3){
if (opt.at(2) == "-r"){
qout << "Device release (RnD) is currently: " << ssu.release(true) << endl;
state = Idle;
} else {
qout << "Changing release from " << ssu.release()
<< " to " << opt.at(2) << endl;
227
qout << "Your device is now in release mode!" << endl;
228
ssu.setRelease(opt.at(2));
229
230
231
232
233
SsuRepoManager repoManager;
repoManager.update();
uidWarning();
234
235
236
237
238
239
240
241
state = Idle;
}
} else if (opt.count() == 2) {
qout << "Device release is currently: " << ssu.release() << endl;
state = Idle;
} else if (opt.count() == 4 && opt.at(2) == "-r"){
qout << "Changing release (RnD) from " << ssu.release(true)
<< " to " << opt.at(3) << endl;
242
qout << "Your device is now in RnD mode!" << endl;
243
ssu.setRelease(opt.at(3), true);
244
245
246
247
248
SsuRepoManager repoManager;
repoManager.update();
uidWarning();
249
250
251
252
253
state = Idle;
}
}
void RndSsuCli::optRepos(QStringList opt){
254
QTextStream qout(stdout);
255
256
SsuRepoManager repoManager;
SsuDeviceInfo deviceInfo;
257
258
QHash<QString, QString> repoParameters, repoOverride;
QString device="";
259
bool rndRepo = false;
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
int micMode=0, flagStart = 0;
if ((ssu.deviceMode() & Ssu::RndMode) == Ssu::RndMode)
rndRepo = true;
if (opt.count() >= 3 && opt.at(2) == "-m"){
micMode = 1;
// TODO: read the default mic override variables from some config
/*
repoOverride.insert("release", "@RELEASE@");
repoOverride.insert("rndRelease", "@RNDRELEASE@");
repoOverride.insert("flavour", "@FLAVOUR@");
repoOverride.insert("arch", "@ARCH@");
*/
}
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
if (opt.count() >= 3 + micMode){
// first argument is flag
if (opt.at(2 + micMode).contains("=")){
flagStart = 2 + micMode;
} else if (!opt.at(2 + micMode).contains("=") &&
opt.count() == 3 + micMode) {
// first (and only) argument is device)
device = opt.at(2 + micMode);
} else if(!opt.at(2 + micMode).contains("=") &&
opt.count() > 3 + micMode &&
opt.at(3 + micMode).contains("=")){
// first argument is device, second flag
device = opt.at(2 + micMode);
flagStart = 3 + micMode;
} else {
state = UserError;
return;
}
}
295
296
297
298
299
300
301
302
303
304
305
306
if (flagStart != 0){
for (int i=flagStart; i<opt.count(); i++){
if (opt.at(i).count("=") != 1){
qout << "Invalid flag: " << opt.at(i) << endl;
state = Idle;
return;
}
QStringList split = opt.at(i).split("=");
repoOverride.insert(split.at(0), split.at(1));
}
}
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
if (repoOverride.contains("rnd")){
if (repoOverride.value("rnd") == "true")
rndRepo = true;
else if (repoOverride.value("rnd") == "false")
rndRepo = false;
}
if (device != ""){
deviceInfo.setDeviceModel(device);
repoOverride.insert("model", device);
}
// TODO: rnd mode override needs to be implemented
QStringList repos;
// micMode? handle it and return, as it's a lot simpler than full mode
if (micMode){
325
repos = deviceInfo.repos(rndRepo, SsuRepoManager::BoardFilter);
326
foreach (const QString &repo, repos){
327
328
329
330
331
332
333
334
QString repoName = repo;
if (repo.endsWith("-debuginfo")){
repoName = repo.left(repo.size() - 10);
repoParameters.insert("debugSplit", "debug");
} else if (repoParameters.value("debugSplit") == "debug")
repoParameters.remove("debugSplit");
QString repoUrl = ssu.repoUrl(repoName, rndRepo, repoParameters, repoOverride);
335
336
337
338
339
340
341
342
343
344
qout << "repo --name=" << repo << "-"
<< (rndRepo ? repoOverride.value("rndRelease")
: repoOverride.value("release"))
<< " --baseurl=" << repoUrl << endl;
}
state = Idle;
return;
}
if (device.isEmpty())
345
repos = deviceInfo.repos(rndRepo, SsuRepoManager::BoardFilterUserBlacklist);
346
347
else {
qout << "Printing repository configuration for '" << device << "'" << endl << endl;
348
repos = deviceInfo.repos(rndRepo, SsuRepoManager::BoardFilter);
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
}
SsuCoreConfig *ssuSettings = SsuCoreConfig::instance();
qout << "Enabled repositories (global): " << endl;
for (int i=0; i<=3; i++){
// for each repository, print repo and resolve url
int longestField = 0;
foreach (const QString &repo, repos)
if (repo.length() > longestField)
longestField = repo.length();
qout.setFieldAlignment(QTextStream::AlignLeft);
foreach (const QString &repo, repos){
364
365
366
367
368
369
370
371
QString repoName = repo;
if (repo.endsWith("-debuginfo")){
repoName = repo.left(repo.size() - 10);
repoParameters.insert("debugSplit", "debug");
} else if (repoParameters.value("debugSplit") == "debug")
repoParameters.remove("debugSplit");
QString repoUrl = ssu.repoUrl(repoName, rndRepo, repoParameters, repoOverride);
372
373
374
375
376
377
378
379
qout << " - " << qSetFieldWidth(longestField) << repo << qSetFieldWidth(0) << " ... " << repoUrl << endl;
}
if (i==0){
if (device != ""){
repos.clear();
continue;
}
380
repos = deviceInfo.repos(rndRepo, SsuRepoManager::UserFilter);
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
qout << endl << "Enabled repositories (user): " << endl;
} else if (i==1){
repos = deviceInfo.disabledRepos();
if (device.isEmpty())
qout << endl << "Disabled repositories (global, might be overridden by user config): " << endl;
else
qout << endl << "Disabled repositories (global): " << endl;
} else if (i==2) {
repos.clear();
if (device != "")
continue;
if (ssuSettings->contains("disabled-repos"))
repos.append(ssuSettings->value("disabled-repos").toStringList());
qout << endl << "Disabled repositories (user): " << endl;
}
396
397
398
}
state = Idle;
399
400
401
402
}
void RndSsuCli::optStatus(){
QTextStream qout(stdout);
403
SsuDeviceInfo deviceInfo;
404
405
406
407
408
409
/*
* print device information and registration status
*/
qout << "Device registration status: "
<< (ssu.isRegistered() ? "registered" : "not registered") << endl;
410
qout << "Device model: " << deviceInfo.deviceModel() << endl;
411
412
if (deviceInfo.deviceVariant() != "")
qout << "Device variant: " << deviceInfo.deviceVariant() << endl;
413
qout << "Device UID: " << deviceInfo.deviceUid() << endl;
414
415
416
417
if ((ssu.deviceMode() & Ssu::RndMode) == Ssu::RndMode)
qout << "Release (rnd): " << ssu.release(true) << " (" << ssu.flavour() << ")" << endl;
else
qout << "Release: " << ssu.release() << endl;
418
419
}
420
void RndSsuCli::optUpdateCredentials(QStringList opt){
421
422
423
424
425
QTextStream qout(stdout);
/*
* update the credentials
* optional argument: -f
*/
426
427
428
429
bool force=false;
if (opt.count() == 3 && opt.at(2) == "-f")
force=true;
430
431
if (!ssu.isRegistered()){
qout << "Device is not registered, can't update credentials" << endl;
432
state = Idle;
433
QCoreApplication::exit(1);
434
} else {
435
ssu.updateCredentials(force);
436
437
438
state = Busy;
}
}
439
440
441
442
void RndSsuCli::optUpdateRepos(){
SsuRepoManager repoManager;
repoManager.update();
443
uidWarning();
444
445
}
446
447
448
449
450
void RndSsuCli::run(){
QTextStream qout(stdout);
QStringList arguments = QCoreApplication::arguments();
451
452
453
if (arguments.at(0).endsWith("rndssu"))
qout << "NOTE: this binary is now called ssu. The rndssu symlink will go away after some time" << endl;
454
455
456
457
458
// make sure there's a first argument to parse
if (arguments.count() < 2){
usage();
return;
}
459
460
461
462
// everything without additional arguments gets handled here
// functions with arguments need to take care of argument validation themselves
if (arguments.count() == 2){
463
if (arguments.at(1) == "status" || arguments.at(1) == "s")
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
optStatus();
else if (arguments.at(1) == "updaterepos" || arguments.at(1) == "ur")
optUpdateRepos();
else
state = UserError;
} else if (arguments.count() >= 3){
if (arguments.at(1) == "addrepo" || arguments.at(1) == "ar")
optModifyRepo(Add, arguments);
else if (arguments.at(1) == "removerepo" || arguments.at(1) == "rr")
optModifyRepo(Remove, arguments);
else if (arguments.at(1) == "enablerepo" || arguments.at(1) == "er")
optModifyRepo(Enable, arguments);
else if (arguments.at(1) == "disablerepo" || arguments.at(1) == "dr")
optModifyRepo(Disable, arguments);
else
state = UserError;
480
} else
481
482
483
484
state = UserError;
// functions accepting 0 or more arguments; those need to set state to Idle
// on success
485
486
487
if (arguments.at(1) == "register" || arguments.at(1) == "r")
optRegister(arguments);
else if (arguments.at(1) == "repos" || arguments.at(1) == "lr")
488
489
490
491
492
optRepos(arguments);
else if (arguments.at(1) == "flavour" || arguments.at(1) == "fl")
optFlavour(arguments);
else if (arguments.at(1) == "mode" || arguments.at(1) == "m")
optMode(arguments);
493
494
else if (arguments.at(1) == "model" || arguments.at(1) == "mo")
optModel(arguments);
495
496
497
498
else if (arguments.at(1) == "release" || arguments.at(1) == "re")
optRelease(arguments);
else if (arguments.at(1) == "update" || arguments.at(1) == "up")
optUpdateCredentials(arguments);
499
500
else if (arguments.at(1) == "domain")
optDomain(arguments);
501
502
503
504
505
// functions that need to wait for a response from ssu should set a flag so
// we can do default exit catchall here
if (state == Idle)
QCoreApplication::exit(0);
else if (state == UserError)
506
507
508
usage();
}
509
510
511
512
513
514
515
516
517
518
void RndSsuCli::uidWarning(QString message){
if (message.isEmpty())
message = "Run 'ssu ur' as root to recreate repository files";
if (geteuid() != 0){
QTextStream qout(stderr);
qout << "You're not root. " << message << endl;
}
}
519
void RndSsuCli::usage(){
520
QTextStream qout(stderr);
521
522
523
524
525
526
527
528
qout << "\nUsage: ssu <command> [-command-options] [arguments]" << endl
<< endl
<< "Repository management:" << endl
<< "\tupdaterepos, ur \tupdate repository files" << endl
<< "\trepos, lr \tlist configured repositories" << endl
<< "\t [-m] \tformat output suitable for kickstart" << endl
<< "\t [device] \tuse repos for 'device'" << endl
<< "\t [flags] \tadditional flags" << endl
529
<< "\t rnd=<true|false> \tset rnd or release mode (default: take from host)" << endl
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
<< "\taddrepo, ar <repo> \tadd this repository" << endl
<< "\t [url] \tspecify URL, if not configured" << endl
<< "\tremoverepo, rr <repo> \tremove this repository from configuration" << endl
<< "\tenablerepo, er <repo> \tenable this repository" << endl
<< "\tdisablerepo, dr <repo> \tdisable this repository" << endl
<< endl
<< "Configuration management:" << endl
<< "\tflavour, fl \tdisplay flavour used (RnD only)" << endl
<< "\t [newflavour] \tset new flavour" << endl
<< "\trelease, re \tdisplay release used" << endl
<< "\t [-r] \tuse RnD release" << endl
<< "\t [newrelease] \tset new (RnD)release" << endl
<< endl
<< "Device management:" << endl
<< "\tstatus, s \tprint registration status and device information" << endl
<< "\tregister, r \tregister this device" << endl
546
<< "\t [-h] \tconfigure user for OBS home" << endl
547
548
<< "\tupdate, up \tupdate repository credentials" << endl
<< "\t [-f] \tforce update" << endl
549
<< "\tmodel, mo \tprint name of device model (like N9)" << endl
550
551
<< endl;
qout.flush();
552
553
QCoreApplication::exit(1);
}