forked from gazelle-installer/gazelle-installer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
minstall.cpp
executable file
·3607 lines (3268 loc) · 158 KB
/
minstall.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//
// Copyright (C) 2003-2010 by Warren Woodford
// Heavily edited, with permision, by anticapitalista for antiX 2011-2014.
// Heavily revised by dolphin oracle, adrian, and anticaptialista 2018.
// additional mount and compression oftions for btrfs by rob 2018
// Major GUI update and user experience improvements by AK-47 2019.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include <QDebug>
#include <QFileInfo>
#include <QTimer>
#include <QProcess>
#include <QProcessEnvironment>
#include <QTimeZone>
#include <fcntl.h>
#include <sys/statvfs.h>
#include <sys/stat.h>
#include "version.h"
#include "minstall.h"
MInstall::MInstall(const QStringList &args, const QString &cfgfile)
: proc(this)
{
setupUi(this);
listLog->addItem("Version " VERSION);
proc.setupUI(listLog, progressBar);
updateCursor(Qt::WaitCursor);
setWindowFlags(Qt::Window); // for the close, min and max buttons
installBox->hide();
oobe = args.contains("--oobe");
pretend = args.contains("--pretend");
nocopy = args.contains("--nocopy");
sync = args.contains("--sync");
if(!oobe) {
brave = args.contains("--brave");
automatic = args.contains("--auto");
oem = args.contains("--oem");
gptoverride = args.contains("--gpt-override");
} else {
brave = automatic = oem = gptoverride = false;
closeButton->setText(tr("Shutdown"));
phase = 2;
// dark palette for the OOBE screen
QColor charcoal(56, 56, 56);
QPalette pal;
pal.setColor(QPalette::Window, charcoal);
pal.setColor(QPalette::WindowText, Qt::white);
pal.setColor(QPalette::Base, charcoal.darker());
pal.setColor(QPalette::AlternateBase, charcoal);
pal.setColor(QPalette::Text, Qt::white);
pal.setColor(QPalette::Button, charcoal);
pal.setColor(QPalette::ButtonText, Qt::white);
pal.setColor(QPalette::Active, QPalette::Button, charcoal);
pal.setColor(QPalette::Disabled, QPalette::Light, charcoal.darker());
pal.setColor(QPalette::Disabled, QPalette::Text, Qt::darkGray);
pal.setColor(QPalette::Disabled, QPalette::WindowText, Qt::darkGray);
pal.setColor(QPalette::Disabled, QPalette::ButtonText, Qt::darkGray);
pal.setColor(QPalette::Highlight, Qt::lightGray);
pal.setColor(QPalette::HighlightedText, Qt::black);
pal.setColor(QPalette::ToolTipBase, Qt::black);
pal.setColor(QPalette::ToolTipText, Qt::white);
pal.setColor(QPalette::Link, Qt::cyan);
qApp->setPalette(pal);
}
if (pretend) listHomes = args; // dummy existing homes
// setup system variables
QSettings settings("/usr/share/gazelle-installer-data/installer.conf", QSettings::NativeFormat);
PROJECTNAME = settings.value("PROJECT_NAME").toString();
PROJECTSHORTNAME = settings.value("PROJECT_SHORTNAME").toString();
PROJECTVERSION = settings.value("VERSION").toString();
PROJECTURL = settings.value("PROJECT_URL").toString();
PROJECTFORUM = settings.value("FORUM_URL").toString();
INSTALL_FROM_ROOT_DEVICE = settings.value("INSTALL_FROM_ROOT_DEVICE").toBool();
DEFAULT_HOSTNAME = settings.value("DEFAULT_HOSTNAME").toString();
ENABLE_SERVICES = settings.value("ENABLE_SERVICES").toStringList();
POPULATE_MEDIA_MOUNTPOINTS = settings.value("POPULATE_MEDIA_MOUNTPOINTS").toBool();
MIN_INSTALL_SIZE = settings.value("MIN_INSTALL_SIZE").toString();
PREFERRED_MIN_INSTALL_SIZE = settings.value("PREFERRED_MIN_INSTALL_SIZE").toString();
REMOVE_NOSPLASH = settings.value("REMOVE_NOSPLASH", "false").toBool();
setWindowTitle(tr("%1 Installer").arg(PROJECTNAME));
gotoPage(0);
// config file
config = new MSettings(cfgfile, this);
// Link block
QString link_block;
settings.beginGroup("LINKS");
QStringList links = settings.childKeys();
for (const QString &link : links) {
link_block += "\n\n" + tr(link.toUtf8().constData()) + ": " + settings.value(link).toString();
}
settings.endGroup();
// set some distro-centric text
remindersBrowser->setPlainText(tr("Support %1\n\n%1 is supported by people like you. Some help others at the support forum - %2, or translate help files into different languages, or make suggestions, write documentation, or help test new software.").arg(PROJECTNAME, PROJECTFORUM)
+ "\n" + link_block);
// ensure the help widgets are displayed correctly when started
// Qt will delete the heap-allocated event object when posted
qApp->postEvent(this, new QEvent(QEvent::PaletteChange));
QTimer::singleShot(0, this, &MInstall::startup);
}
MInstall::~MInstall() {
}
// meant to be run after the installer becomes visible
void MInstall::startup()
{
proc.log(__PRETTY_FUNCTION__);
if (oobe) {
containsSystemD = QFileInfo("/usr/bin/systemctl").isExecutable();
saveDesktopCheckBox->hide();
} else {
containsSystemD = QFileInfo("/live/aufs/bin/systemctl").isExecutable();
rootSources = "/live/aufs/bin /live/aufs/dev"
" /live/aufs/etc /live/aufs/lib /live/aufs/lib64 /live/aufs/media /live/aufs/mnt"
" /live/aufs/opt /live/aufs/root /live/aufs/sbin /live/aufs/selinux /live/aufs/usr"
" /live/aufs/var /live/aufs/home";
//load some live variables
QSettings livesettings("/live/config/initrd.out",QSettings::NativeFormat);
SQFILE_FULL = livesettings.value("SQFILE_FULL", "/live/boot-dev/antiX/linuxfs").toString();
isRemasteredDemoPresent = checkForRemaster();
// calculate required disk space
bootSource = "/live/aufs/boot";
bootSpaceNeeded = proc.execOut("du -sb " + bootSource).section('\t', 0, 0).toLongLong();
//rootspaceneeded is the size of the linuxfs file * a compression factor + contents of the rootfs. conservative but fast
//factors are same as used in live-remaster
//get compression factor by reading the linuxfs squasfs file, if available
qDebug() << "linuxfs file is at : " << SQFILE_FULL;
long long compression_factor;
QString linuxfs_compression_type = "xz"; //default conservative
if (QFileInfo::exists(SQFILE_FULL)) {
linuxfs_compression_type = proc.execOut("dd if=" + SQFILE_FULL + " bs=1 skip=20 count=2 status=none 2>/dev/null| od -An -tdI");
}
//gzip, xz, or lz4
if ( linuxfs_compression_type == "1") {
compression_factor = 37; // gzip
} else if (linuxfs_compression_type == "2") {
compression_factor = 52; //lzo, not used by antiX
} else if (linuxfs_compression_type == "3") {
compression_factor = 52; //lzma, not used by antiX
} else if (linuxfs_compression_type == "4") {
compression_factor = 31; //xz
} else if (linuxfs_compression_type == "5") {
compression_factor = 52; // lz4
} else {
compression_factor = 30; //anythng else or linuxfs not reachable (toram), should be pretty conservative
}
qDebug() << "linuxfs compression type is " << linuxfs_compression_type << "compression factor is " << compression_factor;
long long rootfs_file_size = 0;
long long linuxfs_file_size = proc.execOut("df /live/linux --output=used --total |tail -n1").toLongLong() * 1024 * 100 / compression_factor;
if (QFileInfo::exists("/live/perist-root")) {
rootfs_file_size = proc.execOut("df /live/persist-root --output=used --total |tail -n1").toLongLong() * 1024;
}
qDebug() << "linuxfs file size is " << linuxfs_file_size << " rootfs file size is " << rootfs_file_size;
//add rootfs file size to the calculated linuxfs file size. probaby conservative, as rootfs will likely have some overlap with linuxfs
long long safety_factor = 128 * 1024 * 1024; // 128 MB safety factor
rootSpaceNeeded = linuxfs_file_size + rootfs_file_size + safety_factor;
if (!(bootSpaceNeeded)) {
QMessageBox::warning(this, windowTitle(),
tr("Cannot access source medium.\nActivating pretend installation."));
pretend = true;
}
const long long spaceBlock = 134217728; // 128MB
bootSpaceNeeded += 2*spaceBlock - (bootSpaceNeeded % spaceBlock);
qDebug() << "Minimum space:" << bootSpaceNeeded << "(boot)," << rootSpaceNeeded << "(root)";
// uefi = false if not uefi, or if a bad combination, like 32 bit iso and 64 bit uefi)
if (proc.exec("uname -m | grep -q i686", false) && proc.exec("grep -q 64 /sys/firmware/efi/fw_platform_size")) {
uefi = false;
} else {
uefi = proc.exec("test -d /sys/firmware/efi", true);
}
qDebug() << "uefi =" << uefi;
autoMountEnabled = true; // disable auto mount by force
if (!pretend) setupAutoMount(false);
// advanced encryption settings page defaults
on_comboFDEcipher_currentIndexChanged(comboFDEcipher->currentText());
on_comboFDEchain_currentIndexChanged(comboFDEchain->currentText());
on_comboFDEivgen_currentIndexChanged(comboFDEivgen->currentText());
rootLabelEdit->setText("root" + PROJECTSHORTNAME + PROJECTVERSION);
homeLabelEdit->setText("home" + PROJECTSHORTNAME);
swapLabelEdit->setText("swap" + PROJECTSHORTNAME);
rootTypeCombo->setEnabled(false);
homeTypeCombo->setEnabled(false);
checkBoxEncryptRoot->setEnabled(false);
checkBoxEncryptHome->setEnabled(false);
rootLabelEdit->setEnabled(false);
homeLabelEdit->setEnabled(false);
swapLabelEdit->setEnabled(false);
FDEpassword->hide();
FDEpassword2->hide();
labelFDEpass->hide();
labelFDEpass2->hide();
buttonAdvancedFDE->hide();
gbEncrPass->hide();
existing_partitionsButton->hide();
//disable encryption in gui if cryptsetup not present
QFileInfo cryptsetup("/sbin/cryptsetup");
QFileInfo crypsetupinitramfs("/usr/share/initramfs-tools/conf-hooks.d/cryptsetup");
if ( !cryptsetup.exists() && !cryptsetup.isExecutable() && !crypsetupinitramfs.exists()) {
checkBoxEncryptAuto->hide();
checkBoxEncryptHome->hide();
checkBoxEncryptRoot->hide();
checkBoxEncryptSwap->hide();
buttonAdvancedFDE->hide();
buttonAdvancedFDECust->hide();
labelEncrypt->hide();
}
// Detect snapshot-backup account(s)
haveSnapshotUserAccounts = checkForSnapshot();
}
// set default host name
computerNameEdit->setText(DEFAULT_HOSTNAME);
setupkeyboardbutton();
// timezone lists
listTimeZones = proc.execOutLines("find -L /usr/share/zoneinfo/posix -mindepth 2 -type f -printf %P\\n", true);
cmbTimeArea->clear();
for (const QString &zone : listTimeZones) {
const QString &area = zone.section('/', 0, 0);
if (cmbTimeArea->findData(QVariant(area)) < 0) {
QString text(area);
if (area == "Indian" || area == "Pacific"
|| area == "Atlantic" || area == "Arctic") text.append(" Ocean");
cmbTimeArea->addItem(text, area);
}
}
cmbTimeArea->model()->sort(0);
// locale list
localeCombo->clear();
QStringList loclist = proc.execOutLines("locale -a | grep -Ev '^(C|POSIX)\\.?' | grep -E 'utf8|UTF-8'");
for (QString &strloc : loclist) {
strloc.replace("utf8", "UTF-8", Qt::CaseInsensitive);
QLocale loc(strloc);
localeCombo->addItem(loc.nativeCountryName() + " - " + loc.nativeLanguageName(), QVariant(strloc));
}
localeCombo->model()->sort(0);
// default locale selection
int ixLocale = localeCombo->findData(QVariant(QLocale::system().name() + ".UTF-8"));
if (localeCombo->currentIndex() != ixLocale) localeCombo->setCurrentIndex(ixLocale);
else on_localeCombo_currentIndexChanged(ixLocale);
// if it looks like an apple...
if (proc.exec("grub-probe -d /dev/sda2 2>/dev/null | grep hfsplus", false)) {
mactest = true;
localClockCheckBox->setChecked(true);
}
//check for samba
QFileInfo info("/etc/init.d/smbd");
if (!info.exists()) {
computerGroupLabel->setEnabled(false);
computerGroupEdit->setEnabled(false);
computerGroupEdit->setText("");
sambaCheckBox->setChecked(false);
sambaCheckBox->setEnabled(false);
}
// check for the Samba server
QString val = proc.execOut("dpkg -s samba | grep '^Status.*ok.*' | sed -e 's/.*ok //'");
haveSamba = (val.compare("installed") == 0);
buildServiceList();
if (oobe) manageConfig(ConfigLoadB);
else {
updatePartitionWidgets();
manageConfig(ConfigLoadA);
stashAdvancedFDE(true);
}
stashServices(true);
if (oobe) gotoPage(7); // go to Network page
else {
copyrightBrowser->setPlainText(tr("%1 is an independent Linux distribution based on Debian Stable.\n\n"
"%1 uses some components from MEPIS Linux which are released under an Apache free license."
" Some MEPIS components have been modified for %1.\n\nEnjoy using %1").arg(PROJECTNAME));
gotoPage(1);
}
updateCursor();
// automatic installation
if (automatic) nextButton->click();
}
// turn auto-mount off and on
void MInstall::setupAutoMount(bool enabled)
{
proc.log(__PRETTY_FUNCTION__);
if (autoMountEnabled == enabled) return;
QFileInfo finfo;
// check if the systemctl program is present
bool have_sysctl = false;
const QStringList &envpath = QProcessEnvironment::systemEnvironment().value("PATH").split(':');
for(const QString &path : envpath) {
finfo.setFile(path + "/systemctl");
if (finfo.isExecutable()) {
have_sysctl = true;
break;
}
}
// check if udisksd is running.
bool udisksd_running = false;
if (proc.exec("ps -e | grep 'udisksd'")) udisksd_running = true;
// create a list of rules files that are being temporarily overridden
QStringList udev_temp_mdadm_rules;
finfo.setFile("/run/udev");
if (finfo.isDir()) {
udev_temp_mdadm_rules = proc.execOutLines("egrep -l '^[^#].*mdadm (-I|--incremental)' /lib/udev/rules.d");
for (QString &rule : udev_temp_mdadm_rules) {
rule.replace("/lib/udev", "/run/udev");
}
}
// auto-mount setup
if (!enabled) {
// disable auto-mount
if (have_sysctl) {
// Use systemctl to prevent automount by masking currently unmasked mount points
listMaskedMounts = proc.execOutLines("systemctl list-units --full --all -t mount --no-legend 2>/dev/null | grep -v masked | cut -f1 -d' '"
" | egrep -v '^(dev-hugepages|dev-mqueue|proc-sys-fs-binfmt_misc|run-user-.*-gvfs|sys-fs-fuse-connections|sys-kernel-config|sys-kernel-debug)'").join(' ');
if (!listMaskedMounts.isEmpty()) {
proc.exec("systemctl --runtime mask --quiet -- " + listMaskedMounts);
}
}
// create temporary blank overrides for all udev rules which
// automatically start Linux Software RAID array members
proc.exec("mkdir -p /run/udev/rules.d");
for (const QString &rule : udev_temp_mdadm_rules) {
proc.exec("touch " + rule);
}
if (udisksd_running) {
proc.exec("echo 'SUBSYSTEM==\"block\", ENV{UDISKS_IGNORE}=\"1\"' > /run/udev/rules.d/91-mx-udisks-inhibit.rules");
proc.exec("udevadm control --reload");
proc.exec("udevadm trigger --subsystem-match=block");
}
} else {
// enable auto-mount
if (udisksd_running) {
proc.exec("rm -f /run/udev/rules.d/91-mx-udisks-inhibit.rules");
proc.exec("udevadm control --reload");
proc.exec("partprobe -s");
proc.sleep(1000);
}
// clear the rules that were temporarily overridden
for (const QString &rule : udev_temp_mdadm_rules) {
proc.exec("rm -f " + rule);
}
// Use systemctl to restore that status of any mount points changed above
if (have_sysctl && !listMaskedMounts.isEmpty()) {
proc.exec("systemctl --runtime unmask --quiet -- $MOUNTLIST");
}
}
autoMountEnabled = enabled;
}
/////////////////////////////////////////////////////////////////////////
// util functions
// Check if running inside VirtualBox
bool MInstall::isInsideVB()
{
return proc.exec("lspci -d 80ee:beef | grep -q .", false);
}
bool MInstall::replaceStringInFile(const QString &oldtext, const QString &newtext, const QString &filepath)
{
QString cmd = QString("sed -i 's/%1/%2/g' %3").arg(oldtext, newtext, filepath);
return proc.exec(cmd);
}
void MInstall::updateCursor(const Qt::CursorShape shape)
{
if (shape != Qt::ArrowCursor) {
qApp->setOverrideCursor(QCursor(shape));
} else {
while (qApp->overrideCursor() != nullptr) {
qApp->restoreOverrideCursor();
}
}
qApp->processEvents();
}
bool MInstall::pretendToInstall(int start, int stop)
{
for (int ixi = start; ixi <= stop; ++ixi) {
proc.status(tr("Pretending to install %1").arg(PROJECTNAME), ixi);
proc.sleep(phase == 1 ? 100 : 1000, true);
if (phase < 0) return false;
}
return true;
}
// write out crypttab if encrypting for auto-opening
void MInstall::writeKeyFile()
{
if (phase < 0) return;
// create keyfile
// add key file to luks containers
// get uuid of devices
// write out crypttab
// if encrypt-auto, add home and swap
// if encrypt just home, just add swap
// blkid -s UUID -o value devicename for UUID
// containerName /dev/disk/by-uuid/UUID_OF_PARTITION /root/keyfile luks >>/etc/crypttab
// for auto install, only need to add swap
// for root and home, need to add home and swap
// for root only, add swap
// for home only, add swap
QString rngfile = "/dev/" + comboFDErandom->currentText();
const unsigned int keylength = 4096;
const QLineEdit *passedit = checkBoxEncryptAuto->isChecked() ? FDEpassword : FDEpassCust;
const QByteArray password(passedit->text().toUtf8());
if (isRootEncrypted) { // if encrypting root
bool newkey = (key.length() == 0);
//create keyfile
if (newkey) key.load(rngfile.toUtf8(), keylength);
key.save("/mnt/antiX/root/keyfile", 0400);
//add keyfile to container
QString swapUUID;
if (!swapDevice.isEmpty()) {
swapUUID = proc.execOut("blkid -s UUID -o value " + swapDevice);
proc.exec("cryptsetup luksAddKey " + swapDevice + " /mnt/antiX/root/keyfile",
true, &password);
}
if (isHomeEncrypted && newkey) { // if encrypting separate /home
proc.exec("cryptsetup luksAddKey " + homeDevice + " /mnt/antiX/root/keyfile",
true, &password);
}
QString rootUUID = proc.execOut("blkid -s UUID -o value " + rootDevice);
//write crypttab keyfile entry
QFile file("/mnt/antiX/etc/crypttab");
if (file.open(QIODevice::WriteOnly)) {
QTextStream out(&file);
out << "rootfs /dev/disk/by-uuid/" + rootUUID +" none luks \n";
if (isHomeEncrypted) {
QString homeUUID = proc.execOut("blkid -s UUID -o value " + homeDevice);
out << "homefs /dev/disk/by-uuid/" + homeUUID +" /root/keyfile luks \n";
}
if (!swapDevice.isEmpty()) {
out << "swapfs /dev/disk/by-uuid/" + swapUUID +" /root/keyfile luks,nofail \n";
}
}
file.close();
} else if (isHomeEncrypted) { // if encrypting /home without encrypting root
QString swapUUID;
if (!swapDevice.isEmpty()) {
//create keyfile
key.load(rngfile.toUtf8(), keylength);
key.save("/mnt/antiX/home/.keyfileDONOTdelete", 0400);
key.erase();
//add keyfile to container
swapUUID = proc.execOut("blkid -s UUID -o value " + swapDevice);
proc.exec("cryptsetup luksAddKey " + swapDevice + " /mnt/antiX/home/.keyfileDONOTdelete",
true, &password);
}
QString homeUUID = proc.execOut("blkid -s UUID -o value " + homeDevice);
//write crypttab keyfile entry
QFile file("/mnt/antiX/etc/crypttab");
if (file.open(QIODevice::WriteOnly)) {
QTextStream out(&file);
out << "homefs /dev/disk/by-uuid/" + homeUUID +" none luks \n";
if (!swapDevice.isEmpty()) {
out << "swapfs /dev/disk/by-uuid/" + swapUUID +" /home/.keyfileDONOTdelete luks,nofail \n";
proc.exec("sed -i 's/^CRYPTDISKS_MOUNT.*$/CRYPTDISKS_MOUNT=\"\\/home\"/' /mnt/antiX/etc/default/cryptdisks", false);
}
}
file.close();
}
}
// disable hibernate when using encrypted swap
void MInstall::disablehiberanteinitramfs()
{
if (phase < 0) return;
if (isSwapEncrypted) {
proc.exec("touch /mnt/antiX/initramfs-tools/conf.d/resume");
QFile file("/mnt/antiX/etc/initramfs-tools/conf.d/resume");
if (file.open(QIODevice::WriteOnly)) {
QTextStream out(&file);
out << "RESUME=none";
}
file.close();
}
}
bool MInstall::mountPartition(const QString dev, const QString point, const QString mntops)
{
proc.log(__PRETTY_FUNCTION__);
if (phase < 0) return -1;
mkdir(point.toUtf8(), 0755);
QString cmd = QString("/bin/mount %1 %2 -o %3").arg(dev).arg(point).arg(mntops);
return proc.exec(cmd);
}
// checks SMART status of the selected drives, returns false if it detects errors and user chooses to abort
bool MInstall::checkTargetDrivesOK()
{
proc.log(__PRETTY_FUNCTION__);
if (phase < 0) return false;
QString smartFail, smartWarn;
auto lambdaSMART = [this, &smartFail, &smartWarn](const QString &drv, const QString &purpose) -> void {
proc.exec("smartctl -H /dev/" + drv, true);
if (proc.exitStatus() == MProcess::NormalExit && proc.exitCode() & 8) {
// see smartctl(8) manual: EXIT STATUS (Bit 3)
smartFail += " - " + drv + " (" + purpose + ")\n";
} else {
const QString &output = proc.execOut("smartctl -A /dev/" + drv + "| grep -E \"^ 5|^196|^197|^198\" | awk '{ if ( $10 != 0 ) { print $1,$2,$10} }'");
if (!output.isEmpty()) {
smartWarn += " ---- " + drv + " (" + purpose + ") ---\n" + output + "\n\n";
}
}
};
if (entireDiskButton->isChecked()) {
lambdaSMART(diskCombo->currentData().toString(), tr("target drive"));
} else {
// this loop prevents the same drive being checked multiple times
for (const BlockDeviceInfo &bdinfo : listBlkDevs) {
if (bdinfo.isDrive) {
// list everything this drive is used for
QStringList purposes;
if (bdinfo.name == rootCombo->currentData().toString()) purposes << "root";
if (bdinfo.name == homeCombo->currentData().toString()) purposes << "/home";
if (bdinfo.name == swapCombo->currentData().toString()) purposes << "swap";
if (bdinfo.name == bootCombo->currentData().toString()) purposes << "boot";
// if selected run the SMART tests
if (!purposes.isEmpty()) lambdaSMART(bdinfo.name, purposes.join(", "));
}
}
}
QString msg;
if (!smartFail.isEmpty()) {
msg = tr("The disks with the partitions you selected for installation are failing:")
+ "\n\n" + smartFail + "\n";
}
if (!smartWarn.isEmpty()) {
msg += tr("Smartmon tool output:") + "\n\n" + smartWarn
+ tr("The disks with the partitions you selected for installation pass the SMART monitor test (smartctl),"
" but the tests indicate it will have a higher than average failure rate in the near future.");
}
if (!msg.isEmpty()) {
int ans;
msg += tr("If unsure, please exit the Installer and run GSmartControl for more information.") + "\n\n";
if (!smartFail.isEmpty()) {
msg += tr("Do you want to abort the installation?");
ans = QMessageBox::critical(this, windowTitle(), msg,
QMessageBox::Yes|QMessageBox::Default|QMessageBox::Escape, QMessageBox::No);
if (ans == QMessageBox::Yes) return false;
} else {
msg += tr("Do you want to continue?");
ans = QMessageBox::warning(this, windowTitle(), msg,
QMessageBox::Yes|QMessageBox::Default, QMessageBox::No|QMessageBox::Escape);
if (ans != QMessageBox::Yes) return false;
}
}
return true;
}
// check password length (maybe complexity)
bool MInstall::checkPassword(QLineEdit *passEdit)
{
if (passEdit->text().isEmpty()) {
QMessageBox::critical(this, windowTitle(),
tr("The password needs to be at least\n"
"%1 characters long. Please select\n"
"a longer password before proceeding.").arg("1"));
nextFocus = passEdit;
return false;
}
return true;
}
// process the next phase of installation if possible
bool MInstall::processNextPhase()
{
widgetStack->setEnabled(true);
const int progPhase23 = 94; // start of Phase 2/3 progress bar space
// Phase < 0 = install has been aborted (Phase -2 on close)
if (phase < 0) return false;
// Phase 0 = install not started yet, Phase 1 = install in progress
// Phase 2 = waiting for operator input, Phase 3 = post-install steps
if (phase == 0) { // no install started yet
proc.status(tr("Preparing to install %1").arg(PROJECTNAME), 0);
if (!checkTargetDrivesOK()) return false;
phase = 1; // installation.
// cleanup previous mounts
cleanup(false);
// the core of the installation
if (!pretend) {
bool ok = makePartitions();
if (ok) ok = formatPartitions();
if (!ok) {
failUI(tr("Failed to format required partitions."));
return false;
}
//run blkid -c /dev/null to freshen UUID cache
proc.exec("blkid -c /dev/null", true);
if (!installLinux(progPhase23 - 1)) return false;
} else if (!pretendToInstall(1, progPhase23 - 1)) {
return false;
}
if (widgetStack->currentWidget() != Step_Progress) {
progressBar->setEnabled(false);
proc.status(tr("Paused for required operator input"), progPhase23);
QApplication::beep();
}
phase = 2;
}
if (phase == 2 && widgetStack->currentWidget() == Step_Progress) {
phase = 3;
progressBar->setEnabled(true);
backButton->setEnabled(false);
if (!pretend) {
proc.status(tr("Setting system configuration"), progPhase23);
if (!isInsideVB() && !oobe) {
proc.exec("/bin/mv -f /mnt/antiX/etc/rc5.d/S*virtualbox-guest-utils /mnt/antiX/etc/rc5.d/K01virtualbox-guest-utils >/dev/null 2>&1", false);
proc.exec("/bin/mv -f /mnt/antiX/etc/rc4.d/S*virtualbox-guest-utils /mnt/antiX/etc/rc4.d/K01virtualbox-guest-utils >/dev/null 2>&1", false);
proc.exec("/bin/mv -f /mnt/antiX/etc/rc3.d/S*virtualbox-guest-utils /mnt/antiX/etc/rc3.d/K01virtualbox-guest-utils >/dev/null 2>&1", false);
proc.exec("/bin/mv -f /mnt/antiX/etc/rc2.d/S*virtualbox-guest-utils /mnt/antiX/etc/rc2.d/K01virtualbox-guest-utils >/dev/null 2>&1", false);
proc.exec("/bin/mv -f /mnt/antiX/etc/rcS.d/S*virtualbox-guest-x11 /mnt/antiX/etc/rcS.d/K21virtualbox-guest-x11 >/dev/null 2>&1", false);
}
if (oem) enableOOBE();
else if(!processOOBE()) return false;
manageConfig(ConfigSave);
config->dumpDebug();
proc.exec("/bin/sync", true); // the sync(2) system call will block the GUI
if (!installLoader()) return false;
} else if (!pretendToInstall(progPhase23, 99)){
return false;
}
phase = 4;
proc.status(tr("Cleaning up"), 100);
cleanup();
gotoPage(widgetStack->indexOf(Step_End));
}
return true;
}
void MInstall::manageConfig(enum ConfigAction mode)
{
if (mode == ConfigSave) {
delete config;
config = new MSettings("/mnt/antiX/etc/minstall.conf", this);
}
if (!config) return;
config->bad = false;
if (mode == ConfigSave) {
config->setSave(true);
config->clear();
config->setValue("Version", VERSION);
config->setValue("Product", PROJECTNAME + " " + PROJECTVERSION);
}
if ((mode == ConfigSave || mode == ConfigLoadA) && !oobe) {
config->startGroup("Storage", Step_Disk);
const char *diskChoices[] = {"Drive", "Partitions"};
QRadioButton *diskRadios[] = {entireDiskButton, existing_partitionsButton};
config->manageRadios("Target", 2, diskChoices, diskRadios);
const bool targetDrive = entireDiskButton->isChecked();
if (targetDrive) {
// Disk drive setup
config->manageComboBox("Drive", diskCombo, true);
config->manageCheckBox("DriveEncrypt", checkBoxEncryptAuto);
} else {
// Partition step
config->setGroupWidget(Step_Partitions);
config->manageCheckBox("SaveHome", saveHomeCheck);
config->manageCheckBox("BadBlocksCheck", badblocksCheck);
// Swap space
config->manageComboBox("Swap/Device", swapCombo, true);
config->manageCheckBox("Swap/Encrypt", checkBoxEncryptSwap);
config->manageLineEdit("Swap/Label", swapLabelEdit);
// Tree starting with root (/)
config->beginGroup("Tree");
config->manageComboBox("Device", rootCombo, true);
config->manageCheckBox("Encrypt", checkBoxEncryptRoot);
config->manageComboBox("Type", rootTypeCombo, false);
config->manageLineEdit("Label", rootLabelEdit);
// Boot (/boot)
config->manageComboBox("boot/Device", bootCombo, true);
// Home (/home)
config->manageComboBox("home/Device", homeCombo, true);
config->manageComboBox("home/Type", homeTypeCombo, false);
config->manageCheckBox("home/Encrypt", checkBoxEncryptHome);
config->manageLineEdit("home/Label", homeLabelEdit);
config->endGroup();
}
config->endGroup();
// AES step
config->startGroup("Encryption", Step_FDE);
if (mode != ConfigSave) {
const QString &epass = config->value("Pass").toString();
if (entireDiskButton->isChecked()) {
FDEpassword->setText(epass);
FDEpassword2->setText(epass);
} else {
FDEpassCust->setText(epass);
FDEpassCust2->setText(epass);
}
}
config->manageComboBox("Cipher", comboFDEcipher, false);
config->manageComboBox("ChainMode", comboFDEchain, false);
config->manageComboBox("IVgenerator", comboFDEivgen, false);
config->manageComboBox("IVhash", comboFDEivhash, false);
config->manageSpinBox("KeySize", spinFDEkeysize);
config->manageComboBox("LUKSkeyHash", comboFDEhash, false);
config->manageComboBox("KernelRNG", comboFDErandom, false);
config->manageSpinBox("KDFroundTime", spinFDEroundtime);
config->endGroup();
if (config->isBadWidget(Step_FDE)) {
config->setGroupWidget(targetDrive ? Step_Disk : Step_Partitions);
config->markBadWidget(buttonAdvancedFDE);
config->markBadWidget(buttonAdvancedFDECust);
}
}
if (mode == ConfigSave || mode == ConfigLoadB) {
if (!oobe) {
// GRUB step
config->startGroup("GRUB", Step_Boot);
if(grubCheckBox->isChecked()) {
const char *grubChoices[] = {"MBR", "PBR", "ESP"};
QRadioButton *grubRadios[] = {grubMbrButton, grubPbrButton, grubEspButton};
config->manageRadios("Install", 3, grubChoices, grubRadios);
}
config->manageComboBox("Location", grubBootCombo, true);
config->endGroup();
}
// Services step
config->startGroup("Services", Step_Services);
QTreeWidgetItemIterator it(csView);
while (*it) {
if ((*it)->parent() != nullptr) {
const QString &itext = (*it)->text(0);
const QVariant checkval((*it)->checkState(0) == Qt::Checked);
if (mode == ConfigSave) config->setValue(itext, checkval);
else {
const bool val = config->value(itext, checkval).toBool();
(*it)->setCheckState(0, val ? Qt::Checked : Qt::Unchecked);
}
}
++it;
}
config->endGroup();
// Network step
config->startGroup("Network", Step_Network);
config->manageLineEdit("ComputerName", computerNameEdit);
config->manageLineEdit("Domain", computerDomainEdit);
config->manageLineEdit("Workgroup", computerGroupEdit);
config->manageCheckBox("Samba", sambaCheckBox);
config->endGroup();
// Localization step
config->startGroup("Localization", Step_Localization);
config->manageComboBox("Locale", localeCombo, true);
config->manageCheckBox("LocalClock", localClockCheckBox);
const char *clockChoices[] = {"24", "12"};
QRadioButton *clockRadios[] = {radio24h, radio12h};
config->manageRadios("ClockHours", 2, clockChoices, clockRadios);
if (mode == ConfigSave) {
config->setValue("Timezone", cmbTimeZone->currentData().toString());
} else {
QVariant def(QString(QTimeZone::systemTimeZoneId()));
const int rc = selectTimeZone(config->value("Timezone", def).toString());
if (rc == 1) config->markBadWidget(cmbTimeArea);
else if (rc == 2) config->markBadWidget(cmbTimeZone);
}
config->manageComboBox("Timezone", cmbTimeZone, true);
config->endGroup();
// User Accounts step
config->startGroup("User", Step_User_Accounts);
config->manageLineEdit("Username", userNameEdit);
config->manageCheckBox("Autologin", autologinCheckBox);
config->manageCheckBox("SaveDesktop", saveDesktopCheckBox);
if(oobe) saveDesktopCheckBox->setCheckState(Qt::Unchecked);
const char *oldHomeActions[] = {"Use", "Save", "Delete"};
QRadioButton *oldHomeRadios[] = {radioOldHomeUse, radioOldHomeSave, radioOldHomeDelete};
config->manageRadios("OldHomeAction", 3, oldHomeActions, oldHomeRadios);
if (mode != ConfigSave) {
const QString &upass = config->value("UserPass").toString();
userPasswordEdit->setText(upass);
userPasswordEdit2->setText(upass);
const QString &rpass = config->value("RootPass").toString();
rootPasswordEdit->setText(rpass);
rootPasswordEdit2->setText(rpass);
}
config->endGroup();
}
if (mode == ConfigSave) {
config->sync();
QFile::copy(config->fileName(), "/etc/minstalled.conf");
}
if (config->bad) {
QMessageBox::critical(this, windowTitle(),
tr("Invalid settings found in configuration file (%1)."
" Please review marked fields as you encounter them.").arg(config->fileName()));
}
}
void MInstall::stashAdvancedFDE(bool save)
{
if (save) {
indexFDEcipher = comboFDEcipher->currentIndex();
indexFDEchain = comboFDEchain->currentIndex();
indexFDEivgen = comboFDEivgen->currentIndex();
indexFDEivhash = comboFDEivhash->currentIndex();
iFDEkeysize = spinFDEkeysize->value();
indexFDEhash = comboFDEhash->currentIndex();
indexFDErandom = comboFDErandom->currentIndex();
iFDEroundtime = spinFDEroundtime->value();
} else {
comboFDEcipher->setCurrentIndex(indexFDEcipher);
comboFDEchain->setCurrentIndex(indexFDEchain);
comboFDEivgen->setCurrentIndex(indexFDEivgen);
comboFDEivhash->setCurrentIndex(indexFDEivhash);
spinFDEkeysize->setValue(iFDEkeysize);
comboFDEhash->setCurrentIndex(indexFDEhash);
comboFDErandom->setCurrentIndex(indexFDErandom);
spinFDEroundtime->setValue(iFDEroundtime);
}
}
bool MInstall::formatPartitions()
{
proc.log(__PRETTY_FUNCTION__);
if (phase < 0) return false;
QString rootdev = rootDevice;
QString swapdev = swapDevice;
QString homedev = homeDevice;
// set up LUKS containers
const QByteArray &encPass = (entireDiskButton->isChecked()
? FDEpassword : FDEpassCust)->text().toUtf8();
const QString &statup = tr("Setting up LUKS encrypted containers");
if (isSwapEncrypted) {
if (swapFormatSize) {
proc.status(statup);
if (!makeLuksPartition(swapdev, encPass)) return false;
}
proc.status(statup);
if (!openLuksPartition(swapdev, "swapfs", encPass)) return false;
swapdev = "/dev/mapper/swapfs";
}
if (isRootEncrypted) {
if (rootFormatSize) {
proc.status(statup);
if (!makeLuksPartition(rootdev, encPass)) return false;
}
proc.status(statup);
if (!openLuksPartition(rootdev, "rootfs", encPass)) return false;
rootdev = "/dev/mapper/rootfs";
}
if (isHomeEncrypted) {
if (homeFormatSize) {
proc.status(statup);
if (!makeLuksPartition(homedev, encPass)) return false;
}
proc.status(statup);
if (!openLuksPartition(homedev, "homefs", encPass)) return false;
homedev = "/dev/mapper/homefs";
}
//if no swap is chosen do nothing
if (swapFormatSize) {
proc.status(tr("Formatting swap partition"));
QString cmd("/sbin/mkswap " + swapdev);
const QString &mkswaplabel = swapLabelEdit->text();
if (!mkswaplabel.isEmpty()) cmd.append(" -L \"" + mkswaplabel + "\"");
if (!proc.exec(cmd, true)) return false;
}
// maybe format root (if not saving /home on root), or if using --sync option
if (rootFormatSize) {
proc.status(tr("Formatting the / (root) partition"));
if (!makeLinuxPartition(rootdev, rootTypeCombo->currentText(),
badblocksCheck->isChecked(), rootLabelEdit->text())) {
return false;
}
}
if (!mountPartition(rootdev, "/mnt/antiX", root_mntops)) return false;
// format and mount /boot if different than root
if (bootFormatSize) {
proc.status(tr("Formatting boot partition"));
if (!makeLinuxPartition(bootDevice, "ext4", false, "boot")) return false;
}
// format ESP if necessary
if (espFormatSize) {
proc.status(tr("Formatting EFI System Partition"));
if (!proc.exec("mkfs.msdos -F 32 " + espDevice)) return false;
proc.exec("parted -s " + BlockDeviceInfo::split(espDevice).at(0) + " set 1 esp on"); // sets boot flag and esp flag
proc.sleep(1000);
}
// maybe format home
if (homeFormatSize) {
proc.status(tr("Formatting the /home partition"));
if (!makeLinuxPartition(homedev, homeTypeCombo->currentText(),
badblocksCheck->isChecked(), homeLabelEdit->text())) {
return false;
}
proc.exec("/bin/rm -r /mnt/antiX/home", true);
}
mkdir("/mnt/antiX/home", 0755);
if (homedev != rootDevice) {
// not on root
proc.status(tr("Mounting the /home partition"));
if (!mountPartition(homedev, "/mnt/antiX/home", home_mntops)) return false;
}
return true;
}
bool MInstall::makeLinuxPartition(const QString &dev, const QString &type, bool chkBadBlocks, const QString &label)
{
proc.log(__PRETTY_FUNCTION__);
if (phase < 0) return false;
QString homedev = homeDevice;
boot_mntops = "defaults,noatime";
if (homedev == dev || dev == "/dev/mapper/homefs") { // if formatting /home partition
home_mntops = "defaults,noatime";
} else {
root_mntops = "defaults,noatime";
}
QString cmd;
if (type == "reiserfs") {
cmd = "mkfs.reiserfs -q";
} else if (type == "reiser4") {
cmd = "mkfs.reiser4 -f -y";
} else if (type.startsWith("btrfs")) {
// btrfs and set up fsck
proc.exec("/bin/cp -fp /bin/true /sbin/fsck.auto");
// set creation options for small drives using btrfs
QString size_str = proc.execOut("/sbin/sfdisk -s " + dev);
long long size = size_str.toLongLong();
size = size / 1024; // in MiB
// if drive is smaller than 6GB, create in mixed mode
if (size < 6000) {
cmd = "mkfs.btrfs -f -M -O skinny-metadata";
} else {
cmd = "mkfs.btrfs -f";