source: src-qt4/pc-installgui/installer.cpp @ 695798b

releng/10.0.1releng/10.0.2releng/10.0.3
Last change on this file since 695798b was 695798b, checked in by Kris Moore <kris@…>, 6 months ago

Add a new PCBSDVERSION #define, which will be set to the current PC-BSD
specific version number we are creating. This number may differ from the
FreeBSD base version when we roll updated ISOs or quarterly package
updates. I.E. 10.0.1

  • Property mode set to 100644
File size: 57.4 KB
Line 
1#include <QCloseEvent>
2#include <QDir>
3#include <QProcess>
4#include <QTimer>
5#include <QTranslator>
6#include <QInputDialog>
7#include <QGraphicsPixmapItem>
8
9#include "backend.h"
10#include "../config.h"
11#include "installer.h"
12#include "helpText.h"
13
14Installer::Installer(QWidget *parent) : QMainWindow(parent)
15{
16    setupUi(this);
17    labelVersion->setText(tr("Version:") + " " + PCBSDVERSION);
18    translator = new QTranslator();
19    haveWarnedSpace=false;
20    force4K = false;
21
22    connect(abortButton, SIGNAL(clicked()), this, SLOT(slotAbort()));
23    connect(backButton, SIGNAL(clicked()), this, SLOT(slotBack()));
24    connect(nextButton, SIGNAL(clicked()), this, SLOT(slotNext()));
25
26    connect(helpButton, SIGNAL(clicked()), this, SLOT(slotHelp()));
27    connect(pushTouchKeyboard, SIGNAL(clicked()), this, SLOT(slotPushVirtKeyboard()));
28    connect(pushChangeKeyLayout, SIGNAL(clicked()), this, SLOT(slotPushKeyLayout()));
29    connect(pushHardware, SIGNAL(clicked()), this, SLOT(slotCheckHardware()));
30    connect(pushNetwork, SIGNAL(clicked()), this, SLOT(slotStartNetworkManager()));
31    connect(pushLoadConfig, SIGNAL(clicked()), this, SLOT(slotLoadConfigUSB()));
32    connect(pushSaveConfig, SIGNAL(clicked()), this, SLOT(slotSaveConfigUSB()));
33    connect(pushSaveConfig2, SIGNAL(clicked()), this, SLOT(slotSaveConfigUSB()));
34
35    //abortButton->setText(tr("&Cancel"));
36    backButton->setText(tr("&Back"));
37    nextButton->setText(tr("&Next"));
38
39    // Init the boot-loader
40    bootLoader = QString("GRUB");
41    // Init the GPT to no
42    loadGPT = false;
43
44    // No optional components by default
45    fSRC=false;
46    fPORTS=false;
47
48    // Load the keyboard info
49    keyModels = Scripts::Backend::keyModels();
50    keyLayouts = Scripts::Backend::keyLayouts();
51
52    // Set the arch name
53    setArch();
54
55    // Start on the first screen
56    installStackWidget->setCurrentIndex(0);
57    backButton->setVisible(false);
58
59    // Update the status bar
60    // This makes the status text more "visible" instead of using the blue background
61    statusBar()->setStyleSheet("background: white");
62}
63
64Installer::~Installer()
65{
66    //delete ui;
67}
68
69bool Installer::isInstalled()
70{
71    return false;
72}
73
74void Installer::setArch()
75{
76   QProcess m;
77   m.start(QString("uname"), QStringList() << "-m");
78   while(m.state() == QProcess::Starting || m.state() == QProcess::Running) {
79      m.waitForFinished(200);
80      QCoreApplication::processEvents();
81   }
82
83   // Get output
84   Arch = m.readLine().simplified();
85   qDebug() << Arch;
86}
87
88void Installer::slotCheckHardware()
89{
90  pcHardware = new dialogCheckHardware();
91  pcHardware->programInit();
92  pcHardware->setWindowModality(Qt::ApplicationModal);
93  pcHardware->show();
94  pcHardware->raise();
95}
96
97void Installer::slotPushKeyLayout()
98{
99  wKey = new widgetKeyboard();
100  wKey->programInit(keyModels, keyLayouts);
101  wKey->setWindowModality(Qt::ApplicationModal);
102  connect(wKey, SIGNAL(saved(QString, QString, QString)), this, SLOT(slotSaveKeyLayout(QString, QString, QString)));
103  wKey->show();
104  wKey->raise();
105}
106
107void Installer::slotSaveKeyLayout(QString model, QString layout, QString variant)
108{
109  curKeyModel = model;
110  curKeyLayout = layout;
111  curKeyVariant = variant;
112  qDebug() << "Changed keyboard layout:" << curKeyModel << curKeyLayout << curKeyVariant;
113}
114
115void Installer::initInstall()
116{
117    // load languages
118    QString langCode;
119    bool foundLang = false;
120    comboLanguage->clear();
121    languages = Scripts::Backend::languages();
122    QString curLang = Scripts::Backend::detectCountryCode(); 
123    for (int i=0; i < languages.count(); ++i) {
124        QString languageStr = languages.at(i);
125        QString language = languageStr.split("-").at(0);
126        comboLanguage->addItem(language.trimmed());
127
128        // Grab the language code
129        langCode = languageStr;
130        langCode.truncate(langCode.lastIndexOf(")"));
131        langCode.remove(0, langCode.lastIndexOf("(") + 1);
132        if ( curLang == langCode ) {
133          comboLanguage->setCurrentIndex(i);
134          foundLang = true;
135        }
136    }
137    connect(comboLanguage, SIGNAL(currentIndexChanged(QString)), this, SLOT(slotChangeLanguage()));
138    // If we found a language from geo-loication, change UI now
139    if ( foundLang )
140       slotChangeLanguage();
141   
142
143    // Load any package scheme data
144    listDeskPkgs = Scripts::Backend::getPackageData(availDesktopPackageData, QString());
145    listServerPkgs = Scripts::Backend::getPackageData(availServerPackageData, QString("trueos"));
146
147    // Do check for available meta-pkgs on boot media
148    if ( QFile::exists("/tmp/no-meta-pkgs") )
149        hasPkgsOnMedia = false;
150    else
151        hasPkgsOnMedia = true;
152
153    qDebug() << "Package Media:" << availDesktopPackageData << hasPkgsOnMedia;
154
155    // See if this media has a freebsd release on it
156    if ( QFile::exists("/tmp/no-fbsd-release") ) {
157        hasFreeBSDOnMedia = false;
158    } else {
159        hasFreeBSDOnMedia = true;
160    }
161       
162    // Do check for install pkgs on boot media
163    if ( QFile::exists("/tmp/no-install-pkgs") ) {
164        hasInstallOnMedia = false;
165    } else {
166        hasInstallOnMedia = true;
167    }
168
169    // Set the key toggle
170    toggleKeyLayout = true;
171
172    // Is this a LIVE disk?
173    if ( QFile::exists("/usr/pcbsd-live") )
174        isLiveMode = true;
175    else
176        isLiveMode = false;
177
178    // Get available memory
179    systemMemory = Scripts::Backend::systemMemory();
180
181    // Load up the keyboard information
182    //connectKeyboardSlots();
183
184    // Connect the disk slots
185    connect(pushDiskCustomize,SIGNAL(clicked()), this, SLOT(slotDiskCustomizeClicked()));
186
187    // Load the disks
188    loadDiskInfo();
189   
190    // Init the desktop wheel
191    initDesktopSelector();
192}
193
194void Installer::loadDiskInfo()
195{
196   sysDisks = Scripts::Backend::hardDrives();
197   if ( sysDisks.empty() ) {
198      QMessageBox::critical(this, tr("PC-BSD Installer"),
199                                tr("Unable to detect any disk drives! The install will now exit."),
200                                QMessageBox::Ok,
201                                QMessageBox::Ok);
202      exit(1);
203   }
204
205   // For now use the first disk we find
206   if ( ! autoGenPartitionLayout(sysDisks.at(0).at(1), true) )
207   {
208      QMessageBox::critical(this, tr("PC-BSD Installer"),
209        tr("Unable to suggest a partition for the detected disk."),
210        QMessageBox::Ok,
211        QMessageBox::Ok);
212   }
213
214   textEditDiskSummary->clear();
215   QStringList summary = getDiskSummary();
216   for ( int i=0; i < summary.count(); ++i)
217     textEditDiskSummary->append(summary.at(i));
218
219   textEditDiskSummary->moveCursor(QTextCursor::Start);
220
221}
222
223// Function which will auto-generate a partition layout based upon the target disk / slice
224bool Installer::autoGenPartitionLayout(QString target, bool isDisk)
225{
226  QString targetType, tmp;
227  int targetLoc, totalSize = 0, mntsize, swapsize;
228  QString targetDisk, targetSlice, tmpPass, fsType;
229  bool ok;
230  ok = false;
231
232  // Clear out the original disk layout
233  sysFinalDiskLayout.clear();
234  QStringList fileSystem;
235  qDebug() << "Generating disk layout";
236
237
238  if ( isDisk ) {
239    targetType = "DRIVE";
240    targetSlice = "ALL";
241    targetDisk = target;
242    targetLoc = 1;
243  } else {
244    targetType = "SLICE";
245    targetDisk = target;
246    targetDisk.truncate(targetDisk.size() -2);
247    targetSlice = target;
248    targetSlice = targetSlice.remove(0, targetSlice.size() -2);
249    targetLoc = 2;
250  }
251 
252  // Lets get the size for this disk / partition
253  for (int i=0; i < sysDisks.count(); ++i) {
254      // Make sure to only add the slices to the listDiskSlices
255      if ( sysDisks.at(i).at(0) == targetType && target == sysDisks.at(i).at(targetLoc))
256        totalSize = sysDisks.at(i).at(targetLoc + 1).toInt(&ok);
257  }
258
259  // Give us a small buffer for rounding errors
260  totalSize = totalSize - 10;
261
262  // Setup some swap space
263  if ( totalSize > 30000 ) {
264    // 2GB if over 30GB of disk space, 512MB otherwise
265    swapsize = 2048;
266  } else {
267    swapsize = 512;
268  }
269  totalSize = totalSize - swapsize;
270
271  // We got a valid size for this disk / slice, lets generate the layout now
272  if( !ok )
273    return false;
274
275  // If over 1.99TB, we should use GPT mode
276  if ( totalSize > 1999900 )
277    loadGPT=true;
278
279  // If on amd64 lets use ZFS, it rox
280  if ( Arch == "amd64" ) {
281    // Add the main zfs pool with standard partitions
282    fsType= "ZFS";
283    fileSystem << targetDisk << targetSlice << "/,/tmp(compress=lz4),/usr(canmount=off),/usr/home(compress=lz4),/usr/jails(compress=lz4),/usr/obj(compress=lz4),/usr/pbi(compress=lz4),/usr/ports(compress=lz4),/usr/ports/distfiles(compress=off),/usr/src(compress=lz4),/var(canmount=off),/var/audit(compress=lz4),/var/log(compress=lz4),/var/tmp(compress=lz4)" << fsType << tmp.setNum(totalSize) << "" << "";
284    sysFinalDiskLayout << fileSystem;
285    fileSystem.clear();
286   
287    // Now add swap space
288    fileSystem << targetDisk << targetSlice << "SWAP" << "SWAP" << tmp.setNum(swapsize) << "" << "";
289    sysFinalDiskLayout << fileSystem;
290    fileSystem.clear();
291
292    //qDebug() << "Auto-Gen FS:" <<  fileSystem;
293    return true;
294  }
295
296  // Looks like not on amd64, fallback to UFS+SUJ and print a nice
297  // warning for the user explaining they *really* want to be on amd64
298  QMessageBox::warning(this, tr("PC-BSD Installer"),
299      tr("Detected that you are running the 32bit version. If your system is 64bit capable (most systems made after 2005), you really should be running the 64bit version"),
300      QMessageBox::Ok,
301      QMessageBox::Ok);
302
303  // Start the UFS layout now
304  mntsize = 2000;
305  fsType="UFS+SUJ";
306
307  fileSystem << targetDisk << targetSlice << "/" << fsType << tmp.setNum(mntsize) << "" << "";
308  totalSize = totalSize - mntsize;
309  //qDebug() << "Auto-Gen FS:" <<  fileSystem;
310  sysFinalDiskLayout << fileSystem;
311  fileSystem.clear();
312   
313
314  fileSystem << targetDisk << targetSlice << "SWAP" << "SWAP" << tmp.setNum(swapsize) << "" << "";
315  totalSize = totalSize - mntsize;
316  //qDebug() << "Auto-Gen FS:" <<  fileSystem;
317  sysFinalDiskLayout << fileSystem;
318  fileSystem.clear();
319
320  // If less than 3GB, skip /var and leave on /
321  if ( totalSize > 3000 ) {
322    // Figure out the default size for /var if we are on FreeBSD / PC-BSD
323    mntsize = 2048;
324    fileSystem << targetDisk << targetSlice << "/var" << fsType << tmp.setNum(mntsize) << "" << "";
325    totalSize = totalSize - mntsize;
326    //qDebug() << "Auto-Gen FS:" <<  fileSystem;
327    sysFinalDiskLayout << fileSystem;
328    fileSystem.clear();
329  }
330
331  // Now use the rest of the disk / slice for /usr
332  fileSystem << targetDisk << targetSlice << "/usr" << fsType << tmp.setNum(totalSize) << "" << "";
333  //qDebug() << "Auto-Gen FS:" <<  fileSystem;
334  sysFinalDiskLayout << fileSystem;
335  fileSystem.clear();
336
337  return true; 
338 
339}
340
341// Function which returns the pc-sysinstall cfg data
342QStringList Installer::getDiskSummary()
343{
344  QList<QStringList> copyList;
345  QStringList summaryList;
346  QString tmp, workingDisk, workingSlice, tmpSlice, XtraTmp, startPart, sliceSize;
347  int disk = 0;
348
349  // Copy over the list to a new variable we can mangle without modifying the original
350  copyList = sysFinalDiskLayout;
351
352  if ( copyList.at(0).at(0) == "MANUAL" )
353  {
354    summaryList << "";
355    summaryList << tr("Installing to file-system mounted at /mnt");
356    return summaryList;
357  }
358
359  // Start our summary
360  summaryList << "";
361  summaryList << "<b>" + tr("The disk will be setup with the following configuration:") + "</b>";
362
363  while ( ! copyList.empty() )
364  {
365    workingDisk = copyList.at(0).at(0);
366    workingSlice = copyList.at(0).at(1);
367    tmpSlice = workingSlice;
368
369    // Check if this is an install to "Unused Space"
370    for (int z=0; z < sysDisks.count(); ++z)
371      if ( sysDisks.at(z).at(0) == "SLICE" \
372        && sysDisks.at(z).at(2) == workingDisk + workingSlice \
373        && sysDisks.at(z).at(4) == "Unused Space" )
374          tmpSlice = "free";
375
376    // Check for any mirror for this device
377    for (int i=0; i < copyList.count(); ++i) {
378       if ( copyList.at(i).at(2).indexOf("MIRROR(" + workingDisk + ")") != -1 )
379       {
380         summaryList << tr("Disk:") + copyList.at(i).at(0) + " " + tr("Mirroring:") + workingDisk;
381         copyList.removeAt(i);
382         break;
383       }
384    }
385
386
387
388    // If after doing the mirror, our list is empty, break out
389    if ( copyList.empty() )
390      break;
391   
392    // If there is a dedicated /boot partition, need to list that first, see what is found
393    for (int i=0; i < copyList.count(); ++i) {
394      QStringList mounts = copyList.at(i).at(2).split(",");
395      for (int z = 0; z < mounts.size(); ++z) {
396        if ( copyList.at(i).at(0) == workingDisk \
397          && copyList.at(i).at(1) == workingSlice \
398          && mounts.at(z) == "/boot" )
399                startPart="/boot";
400      }
401    }
402
403    // If no dedicated /boot partition, then lets list "/" first
404    if(startPart.isEmpty())
405        startPart="/";
406
407    // Start by looking for the root partition
408    for (int i=0; i < copyList.count(); ++i) {
409      QStringList mounts = copyList.at(i).at(2).split(",");
410      for (int z = 0; z < mounts.size(); ++z) {
411        if ( copyList.at(i).at(0) == workingDisk \
412          && copyList.at(i).at(1) == workingSlice \
413          && (mounts.at(z) == startPart || mounts.at(z).indexOf(startPart + "(") == 0 ) ) {
414          // Check if we have any extra arguments to throw on the end
415          XtraTmp="";
416          if ( ! copyList.at(i).at(5).isEmpty() )
417            XtraTmp=" (" + copyList.at(i).at(5) + ")" ;
418
419          // Write the user summary
420          summaryList << "";
421          summaryList << tr("Partition:") + " " + workingDisk + "(" + workingSlice + "):";
422          summaryList << tr("FileSystem:") + " " + copyList.at(i).at(3);
423          summaryList << tr("Size:") + " " + copyList.at(i).at(4) + "MB ";
424          if ( copyList.at(i).at(3) == "ZFS" ) {
425            QStringList zDS = copyList.at(i).at(2).split(",");
426            QString zTMP;
427            for (int ds = 0; ds < zDS.size(); ++ds) {
428              //if ( zDS.at(ds) != "/" )
429              // zDS.replace(ds, "/" + zDS.at(ds));
430              if ( zDS.at(ds).indexOf("(") != -1 ) {
431                zTMP = zDS.at(ds);
432                zTMP.replace("(", " (");
433                zDS.replace(ds, zTMP );
434              }
435            } 
436            if ( radioRestore->isChecked())
437               summaryList << tr("ZFS Datasets:") + " " + tr("The original ZFS layout will be restored");
438            else
439               summaryList << tr("ZFS Datasets:<br>") + " " + zDS.join("<br>");
440          } else {
441            summaryList << tr("Mount:") + " " + copyList.at(i).at(2);
442          }
443          if ( ! XtraTmp.isEmpty() ) {
444            summaryList << tr("Options:") + " " + copyList.at(i).at(5);
445          }
446
447          // Done with this item, remove it now
448          copyList.removeAt(i);
449          break;
450        }
451      }
452    }
453
454
455    // Now look for SWAP
456    for (int i=0; i < copyList.count(); ++i) {
457      if ( copyList.at(i).at(0) == workingDisk \
458        && copyList.at(i).at(1) == workingSlice \
459        && copyList.at(i).at(2) == "SWAP" ) {
460
461        // Write the user summary
462        summaryList << "";
463        summaryList << tr("Partition:") + " " + workingDisk + "(" + workingSlice + "):";
464        summaryList << tr("FileSystem:") + " " + copyList.at(i).at(3);
465        summaryList << tr("Size:") + " " + copyList.at(i).at(4) + "MB ";
466
467        // Done with this item, remove it now
468        copyList.removeAt(i);
469        break;
470      }
471    }
472
473 
474    // Now look for any other partitions
475    int count = copyList.count();
476    for (int i=0; i < count; ++i) {
477      if ( copyList.at(i).at(0) == workingDisk \
478        && copyList.at(i).at(1) == workingSlice ) {
479
480        // Check if we have any extra arguments to throw on the end
481        XtraTmp="";
482        if ( ! copyList.at(i).at(5).isEmpty() )
483          XtraTmp=" (" + copyList.at(i).at(5) + ")" ;
484
485        // If we are working on the last partition, set the size to 0 to use remaining disk
486        if ( i == (count - 1) ) 
487                sliceSize = "0";
488        else
489                sliceSize=copyList.at(i).at(4);
490
491        // Write the user summary
492        summaryList << "";
493        summaryList << tr("Partition:") + " " + workingDisk + "(" + workingSlice + "):";
494
495        summaryList << tr("FileSystem:") + " " + copyList.at(i).at(3);
496
497        summaryList << tr("Size:") + " " + copyList.at(i).at(4) + "MB ";
498        if ( copyList.at(i).at(3) != "ZFS" )
499          summaryList << tr("Mount:") + " " + copyList.at(i).at(2);
500        if ( ! XtraTmp.isEmpty() ) {
501          summaryList << tr("Options:") + " " + copyList.at(i).at(5);
502        }
503
504        // Done with this item, remove it now
505        copyList.removeAt(i);
506        i--;
507        count--;
508      }
509    }
510
511    // Increment our disk counter
512    disk++;
513  }
514
515  return summaryList;
516}
517
518void Installer::slotDiskCustomizeClicked()
519{
520  wDisk = new wizardDisk();
521  wDisk->programInit();
522  wDisk->setWindowModality(Qt::ApplicationModal);
523  if ( radioRestore->isChecked() )
524    wDisk->setRestoreMode();
525  connect(wDisk, SIGNAL(saved(QList<QStringList>, QString, bool, QString, bool)), this, SLOT(slotSaveDiskChanges(QList<QStringList>, QString, bool, QString, bool)));
526  wDisk->show();
527  wDisk->raise();
528}
529
530void Installer::slotDesktopCustomizeClicked()
531{
532  desks = new desktopSelection();
533  if ( radioDesktop->isChecked() )
534     desks->programInit(listDeskPkgs,selectedPkgs);
535  else
536     desks->programInit(listServerPkgs,selectedPkgs);
537  desks->setWindowModality(Qt::ApplicationModal);
538  customPkgsSet = true;
539  connect(desks, SIGNAL(saved(QStringList)), this, SLOT(slotSaveMetaChanges(QStringList)));
540  desks->show();
541  desks->raise();
542}
543
544void Installer::slotSaveMetaChanges(QStringList sPkgs)
545{
546  selectedPkgs = sPkgs;
547
548  if ( radioDesktop->isChecked() )
549     groupDeskSummary->setTitle(tr("PC-BSD Package Selection"));
550  else
551     groupDeskSummary->setTitle(tr("TrueOS Package Selection"));
552
553  textDeskSummary->setText(tr("The following meta-pkgs will be installed:") + "<br>" + selectedPkgs.join("<br>"));
554}
555
556void Installer::slotSaveDiskChanges(QList<QStringList> newSysDisks, QString BL, bool GPT, QString zName, bool zForce )
557{
558
559  bootLoader=BL;
560  zpoolName = zName; 
561  force4K = zForce;
562
563  // Save the new disk layout
564  loadGPT = GPT;
565  sysFinalDiskLayout = newSysDisks;
566  textEditDiskSummary->clear();
567  QStringList summary = getDiskSummary();
568  for ( int i=0; i < summary.count(); ++i)
569    textEditDiskSummary->append(summary.at(i));
570               
571  textEditDiskSummary->moveCursor(QTextCursor::Start);
572 
573  // Regenerate the config
574  startConfigGen();
575}
576
577void Installer::slotChangedMetaPkgSelection()
578{
579
580  selectedPkgs.clear();
581  if ( radioRestore->isChecked() )
582  {
583     pushDeskCustomize->setEnabled(false);
584     textDeskSummary->setText(tr("Performing a restore from a Life-Preserver backup. Click next to start the restore wizard."));
585     return;
586  }
587
588  pushDeskCustomize->setEnabled(true);
589
590  // Set the default desktop meta-pkgs based upon the selection
591  if ( radioDesktop->isChecked() )
592  {
593      selectedPkgs << "KDE" << "KDE-Accessibility" << "KDE-Artwork" << "KDE-Graphics" << "KDE-Multimedia" << "KDE-Network" << "KDE-PIM";
594
595      // Include i18n stuff?
596      if ( comboLanguage->currentIndex() != 0 ) 
597         selectedPkgs << "KDE-L10N";
598
599      // Check if we are using NVIDIA driver and include it automatically
600      QFile file("/var/log/Xorg.0.log");
601      if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
602         
603        QTextStream in(&file);
604        while (!in.atEnd()) {
605           QString line = in.readLine();
606           if ( line.indexOf("NVIDIA") != -1 ) {
607             selectedPkgs << "NVIDIA";
608             break;
609           }
610        }     
611        file.close();
612      } // Done with NVIDIA check
613
614      // Are we on VirtualBox or VMware?
615      QFile filev("/var/log/Xorg.0.log");
616      if (filev.open(QIODevice::ReadOnly | QIODevice::Text)) {
617         
618        QTextStream inv(&filev);
619        while (!inv.atEnd()) {
620           QString line = inv.readLine();
621           if ( line.indexOf("VirtualBox") != -1 ) {
622             selectedPkgs << "VirtualBoxGuest";
623             break;
624           }
625           if ( line.indexOf("VMware") != -1 ) {
626             selectedPkgs << "VMwareGuest";
627             break;
628           }
629        }     
630        filev.close();
631      } // End of VM checks
632  }
633
634  slotSaveMetaChanges(selectedPkgs);
635  qDebug() << selectedPkgs;
636
637}
638
639void Installer::initDesktopSelector()
640{
641    // Using default pkg sets for now
642    customPkgsSet = false;
643    connect(pushDeskCustomize,SIGNAL(clicked()), this, SLOT(slotDesktopCustomizeClicked()));
644    connect(radioDesktop,SIGNAL(clicked()), this, SLOT(slotChangedMetaPkgSelection()));
645    connect(radioServer,SIGNAL(clicked()), this, SLOT(slotChangedMetaPkgSelection()));
646    connect(radioRestore,SIGNAL(clicked()), this, SLOT(slotChangedMetaPkgSelection()));
647    slotChangedMetaPkgSelection();
648}
649
650void Installer::proceed(bool forward)
651{
652    int count = installStackWidget->count() - 1;
653    int index = installStackWidget->currentIndex();
654
655    index = forward ?
656            (index == count ? count : index + 1) :
657            (index == 0 ? 0 : index - 1);
658
659    if ( index > 0 && index != 4)
660      backButton->setVisible(true);
661    else
662      backButton->setVisible(false);
663
664    installStackWidget->setCurrentIndex(index);
665}
666
667// Slot which is called when the Finish button is clicked
668void Installer::slotFinished()
669{
670  qApp->quit();
671}
672
673void Installer::slotSaveFBSDSettings(QString rootPW, QString name, QString userName, QString userPW, QString shell, QString hostname, bool ssh, bool src, bool ports, QStringList netSettings)
674{
675  fRootPW = rootPW;
676  fName = name;
677  fUserName = userName;
678  fUserPW = userPW;
679  fShell = shell;
680  fHost = hostname;
681  fSSH = ssh;
682  fSRC = src;
683  fPORTS = ports;
684  fNetSettings = netSettings;
685  installStackWidget->setCurrentIndex(installStackWidget->currentIndex() + 1);
686
687  // Generate the pc-sysinstall config
688  startConfigGen();
689}
690
691void Installer::slotNext()
692{
693   QString tmp;
694
695   // Update package selection in case user changed language, etc
696   if ( installStackWidget->currentIndex() == 0 && hasPkgsOnMedia && !customPkgsSet) {
697     slotChangedMetaPkgSelection();
698   }
699
700   // If no pkgs on media
701   if ( installStackWidget->currentIndex() == 0 && ! hasPkgsOnMedia) {
702     installStackWidget->setCurrentIndex(2);
703     return;
704   }
705
706   // Start the Restore wizard
707   if ( radioRestore->isChecked() && installStackWidget->currentIndex() == 1 ) { 
708     wRestore = new wizardRestore();
709     wRestore->setWindowModality(Qt::ApplicationModal);
710     wRestore->programInit();
711     connect(wRestore, SIGNAL(saved(QStringList)), this, SLOT(slotSaveRestoreSettings(QStringList)));
712     wRestore->show();
713     wRestore->raise();
714     return ;
715   }
716
717   // Start the TrueOS wizard
718   if ( radioServer->isChecked() && installStackWidget->currentIndex() == 1 ) { 
719     bool tOS;
720     tOS = true;
721
722     wFBSD = new wizardFreeBSD();
723     wFBSD->setWindowModality(Qt::ApplicationModal);
724     wFBSD->programInit(tOS);
725     connect(wFBSD, SIGNAL(saved(QString, QString, QString, QString, QString, QString, bool, bool, bool, QStringList)), this, SLOT(slotSaveFBSDSettings(QString, QString, QString, QString, QString, QString, bool, bool, bool, QStringList)));
726     wFBSD->show();
727     wFBSD->raise();
728     return ;
729   }
730
731   // Create the pc-sysinstall config
732   if ( installStackWidget->currentIndex() == 1 )
733     startConfigGen();
734
735   // If the chosen disk is too small or partition is invalid, don't continue
736   if ( installStackWidget->currentIndex() == 2 && ! checkDiskRequirements())
737      return;
738
739   if ( installStackWidget->currentIndex() == 2 )
740   {
741      startConfigGen();
742      QString msg;
743
744      if (radioRestore->isChecked() )
745        msg=tr("Start the restore now?");
746      else
747        msg=tr("Start the installation now?");
748
749      int ret = QMessageBox::question(this, tr("PC-BSD Installer"),
750                                msg,
751                                QMessageBox::No | QMessageBox::Yes,
752                                QMessageBox::No);
753      switch (ret) {
754      case QMessageBox::Yes:
755          startInstall();
756          break;
757      case QMessageBox::No: // :)
758          return;
759          break;
760      }
761   }
762
763   proceed(true);
764}
765
766void Installer::slotBack()
767{
768   // If no pkgs on media
769   if ( installStackWidget->currentIndex() == 2 && ! hasPkgsOnMedia) {
770        installStackWidget->setCurrentIndex(0);
771        return;
772   }
773
774   proceed(false);
775}
776
777void Installer::slotAbort()
778{
779   close();
780}
781
782void Installer::slotChangeLanguage()
783{
784    if ( comboLanguage->currentIndex() == -1 )
785      return;
786
787    // Figure out the language code
788    QString langCode = languages.at(comboLanguage->currentIndex());
789   
790    // Grab the language code
791    langCode.truncate(langCode.lastIndexOf(")"));
792    langCode.remove(0, langCode.lastIndexOf("(") + 1); 
793
794    // Check what directory our app is in
795    QString appDir;
796    if ( QFile::exists("/usr/local/bin/pc-sysinstaller") )
797      appDir = "/usr/local/share/pcbsd";
798    else
799      appDir = QCoreApplication::applicationDirPath();
800
801    //QTranslator *translator = new QTranslator();
802    qDebug() << "Remove the translator";
803    if ( ! translator->isEmpty() )
804      QCoreApplication::removeTranslator(translator);
805
806    if (translator->load( QString("SysInstaller_") + langCode, appDir + "/i18n/" )) {
807      qDebug() << "Load new Translator" << langCode;
808      QCoreApplication::installTranslator(translator);
809      this->retranslateUi(this);
810    }
811
812    // Change the default keyboard layout
813    if ( langCode == "en" ) {
814       curKeyModel="pc104";
815       curKeyLayout="us";
816       curKeyVariant="";
817       Scripts::Backend::changeKbMap("pc104", "us", "");
818    } else {
819       // TODO - At some point, add additional tests here and set more specific layouts
820       // based upon the language selected
821       curKeyModel="pc105";
822       curKeyLayout=langCode;
823       curKeyVariant="";
824       Scripts::Backend::changeKbMap("pc105", langCode, "" );
825    }
826   
827}
828
829QStringList Installer::getGlobalCfgSettings()
830{
831
832  QStringList tmpList;
833  QString tmp, upDrive;
834
835  tmpList << "# Auto-Generated pc-sysinstall configuration";
836  tmpList << "installInteractive=no";
837
838  // Are we doing a restore?
839  if ( radioRestore->isChecked() )
840  {
841    tmpList << "installMode=zfsrestore";
842    tmpList << "";
843    tmpList << "sshHost=" + restOpts.at(0);
844    tmpList << "sshUser=" + restOpts.at(1);
845    tmpList << "sshPort=" + restOpts.at(2);
846    if ( ! restOpts.at(3).isEmpty() )
847      tmpList << "sshKey=" + restOpts.at(3);
848    tmpList << "zfsProps=" + restOpts.at(4);
849    tmp = restOpts.at(4);
850    tmp.replace(".lp-props-", "");
851    tmp.replace("#", "/");
852    tmpList << "zfsRemoteDataset=" + tmp;
853
854    // Using a custom zpool name?
855    if ( ! zpoolName.isEmpty() )
856      tmpList << "zpoolName=" + zpoolName;
857
858    // Are we force enabling ZFS 4K block sizes?
859    if ( force4K )
860      tmpList << "zfsForce4k=YES";
861
862    tmpList << "";
863    return tmpList;
864  } // End of restore options
865
866
867  if ( sysFinalDiskLayout.at(0).at(0) == "MANUAL" )
868  {
869    tmpList << "installMode=extract";
870    tmpList << "installLocation=/mnt";
871  } else {
872    // Doing a fresh install
873    tmpList << "installMode=fresh";
874  }
875 
876  QString distFiles;
877  distFiles="base doc games kernel";
878  if ( Arch == "amd64" )
879     distFiles+=" lib32";
880
881  // System type we are installing
882  if ( radioDesktop->isChecked() )
883    tmpList << "installType=PCBSD";
884  else
885    tmpList << "installType=FreeBSD";
886
887  tmpList << "packageType=dist";
888
889  // Set the distFiles being used
890  tmpList << "distFiles=" + distFiles;
891  tmpList << "";
892
893  // If we have a custom hostname, add it to the config
894  if ( ! fHost.isEmpty() )
895    tmpList << "hostname=" + fHost;
896
897  // Using a custom zpool name?
898  if ( ! zpoolName.isEmpty() )
899    tmpList << "zpoolName=" + zpoolName;
900
901  // Are we force enabling ZFS 4K block sizes?
902  if ( force4K )
903    tmpList << "zfsForce4k=YES";
904 
905  // Networking setup
906  if ( radioDesktop->isChecked() ) {
907    // PC-BSD network setup
908    tmpList << "netSaveDev=AUTO-DHCP-SLAAC";
909  } else {
910    // FreeBSD network setup
911    if ( fNetSettings.at(0) == "AUTO-DHCP" )
912      tmpList << "netSaveDev=AUTO-DHCP";
913    else if ( fNetSettings.at(0) == "AUTO-DHCP-SLAAC" ) {
914      tmpList << "netSaveDev=AUTO-DHCP-SLAAC";
915      // We cannot rely on SLAAC to provide DNS for example.  The same is true
916      // for DHCP but the worls seems to have forgotten...
917      tmpList << "netSaveIPv6=" + fNetSettings.at(5);
918      tmpList << "netSaveIPv6NameServer=" + fNetSettings.at(6);
919      tmpList << "netSaveIPv6DefaultRouter=" + fNetSettings.at(7);
920    } else if ( fNetSettings.at(0) == "IPv6-SLAAC" ) {
921       tmpList << "netSaveDev=IPv6-SLAAC";
922      // We cannot rely on SLAAC to provide DNS for example.  The same is true
923      // for DHCP but the worls seems to have forgotten...
924      tmpList << "netSaveIPv6=" + fNetSettings.at(5);
925      tmpList << "netSaveIPv6NameServer=" + fNetSettings.at(6);
926      tmpList << "netSaveIPv6DefaultRouter=" + fNetSettings.at(7);
927    } 
928    else
929    {
930      tmp = fNetSettings.at(0);
931      if ( tmp.indexOf(":") > 0 )
932        tmp.truncate(tmp.indexOf(":"));
933      tmpList << "netSaveDev=" + tmp;
934      tmpList << "netSaveIP_" + tmp + "=" + fNetSettings.at(1); 
935      tmpList << "netSaveMask_" + tmp + "=" + fNetSettings.at(2);
936      tmpList << "netSaveNameServer=" + fNetSettings.at(3);
937      tmpList << "netSaveDefaultRouter=" + fNetSettings.at(4);
938      tmpList << "netSaveIPv6=" + fNetSettings.at(5);
939      tmpList << "netSaveIPv6NameServer=" + fNetSettings.at(6);
940      tmpList << "netSaveIPv6DefaultRouter=" + fNetSettings.at(7);
941    }
942  }
943
944
945  // Doing install from /dist directory
946  tmpList << "installMedium=local"; 
947  tmpList << "localPath=/dist";
948
949  if ( comboLanguage->currentIndex() != 0 ) {
950    QString lang = languages.at(comboLanguage->currentIndex());
951    // Grab the language code
952    lang.truncate(lang.lastIndexOf(")"));
953    lang.remove(0, lang.lastIndexOf("(") + 1);
954    tmpList << "";
955    tmpList << "localizeLang=" + lang;
956  }
957
958  // Setup custom keyboard layouts
959  /* KPM
960  tmpList << "";
961  tmpList << "# Keyboard Layout Options";
962  tmp = comboBoxKeyboardModel->currentText();
963  tmp.truncate(tmp.indexOf(")"));
964  tmp = tmp.remove(0, tmp.indexOf(" (") + 2 );
965  tmpList << "localizeKeyModel=" + tmp;
966
967  tmp = listKbLayouts->currentItem()->text();
968  tmp.truncate(tmp.indexOf(")"));
969  tmp = tmp.remove(0, tmp.indexOf(" (") + 2 );
970  tmpList << "localizeKeyLayout=" + tmp;
971
972  tmp = listKbVariants->currentItem()->text();
973  if ( tmp != "<none>" ) {
974    tmp.truncate(tmp.indexOf(")"));
975    tmp = tmp.remove(0, tmp.indexOf(" (") + 2 );
976    tmpList << "localizeKeyVariant=" + tmp;
977  }
978  */
979
980  tmpList << " ";
981
982  return tmpList;
983}
984
985void Installer::startConfigGen()
986{
987
988  if ( ! haveWarnedSpace )
989     checkSpaceWarning();
990
991  QStringList cfgList;
992
993  // Generate the config file now
994  cfgList+=getGlobalCfgSettings();
995
996  cfgList+=getDiskCfgSettings();
997 
998  // We can skip these options if doing a restore
999  if ( ! radioRestore->isChecked() ) {
1000
1001    cfgList+=getComponentCfgSettings();
1002
1003    // Save the install config script to disk
1004    cfgList << "runExtCommand=/root/save-config.sh";
1005
1006    cfgList+= "";
1007
1008    // If doing install from package disk
1009    if ( hasPkgsOnMedia )
1010      cfgList+=getDeskPkgCfg();
1011
1012    cfgList+= "";
1013
1014    if ( radioDesktop->isChecked() ) {
1015      // Doing PC-BSD Install
1016
1017      QString lang;
1018      if ( comboLanguage->currentIndex() != 0 )
1019        lang = languages.at(comboLanguage->currentIndex()).section("(",1,1).section(")",0,0);
1020      else
1021        lang="en_US";
1022
1023      // Setup the desktop
1024      cfgList << "runCommand=sh /usr/local/share/pcbsd/scripts/sys-init.sh desktop " + lang;
1025
1026      // Setup for a fresh system first boot
1027      cfgList << "# Touch flags to enable PC-BSD setup at first boot";
1028      cfgList << "runCommand=touch /var/.runxsetup";
1029      cfgList << "runCommand=touch /var/.pcbsd-firstboot";
1030      cfgList << "runCommand=touch /var/.pcbsd-firstgui";
1031
1032      if ( comboLanguage->currentIndex() != 0 ) {
1033        QString lang = languages.at(comboLanguage->currentIndex());
1034        // Grab the language code
1035        lang.truncate(lang.lastIndexOf(")"));
1036        lang.remove(0, lang.lastIndexOf("(") + 1);
1037        cfgList << "runCommand=echo " + lang + " >/var/.wizardlang";
1038      }
1039
1040      // If the user changed the keyboard layout, save it for first boot
1041      if ( ! curKeyModel.isEmpty() ) {
1042        cfgList << "runCommand=echo \"" + curKeyModel + " " + curKeyLayout + " " + curKeyVariant + "\" >/var/.wizardKeyboard";
1043      }
1044
1045    } else {
1046      // Doing TrueOS Install
1047      cfgList+=getUsersCfgSettings();
1048
1049      // Enable SSH?
1050      if ( fSSH )
1051        cfgList << "runCommand=echo 'sshd_enable=\"YES\"' >>/etc/rc.conf";
1052
1053      // Setup the TrueOS server
1054      cfgList << "runCommand=sh /usr/local/share/pcbsd/scripts/sys-init.sh server";
1055
1056    } 
1057
1058    // Run newaliases to fix mail errors
1059    cfgList << "runCommand=newaliases";
1060
1061  } // End of restore check
1062
1063  // Now write out the cfgList to file
1064  QFile cfgfile( PCSYSINSTALLCFG );
1065  if ( cfgfile.open( QIODevice::WriteOnly ) ) {
1066    QTextStream stream( &cfgfile );
1067    for ( int i=0; i < cfgList.count(); ++i) {
1068      stream <<  cfgList.at(i) << "\n";
1069    }
1070    cfgfile.close();
1071  }
1072}
1073
1074void Installer::slotHelp()
1075{
1076        pcHelp = new dialogHelp();
1077        switch (installStackWidget->currentIndex()) {
1078        case 0:
1079                pcHelp->dialogInit(HELPTEXT0);
1080                break;
1081        case 1:
1082                pcHelp->dialogInit(HELPTEXT1);
1083                break;
1084        case 2:
1085                pcHelp->dialogInit(HELPTEXT2);
1086                break;
1087        case 3:
1088                pcHelp->dialogInit(HELPTEXT3);
1089                break;
1090        default:
1091                pcHelp->dialogInit("No help text...");
1092                break;
1093        }
1094        pcHelp->show();
1095}
1096
1097// Function which returns the pc-sysinstall cfg data
1098QStringList Installer::getDiskCfgSettings()
1099{
1100  QStringList tmpList;
1101  QList<QStringList> copyList;
1102  QString tmp, workingDisk, workingSlice, tmpSlice, XtraTmp, startPart, sliceSize;
1103  int disk = 0;
1104
1105  // Copy over the list to a new variable we can mangle without modifying the original
1106  copyList = sysFinalDiskLayout;
1107
1108  // Doing manual extraction
1109  if ( copyList.at(0).at(0) == "MANUAL" )
1110    return QStringList();
1111
1112  while ( ! copyList.empty() )
1113  {
1114    workingDisk = copyList.at(0).at(0);
1115    workingSlice = copyList.at(0).at(1);
1116    tmpSlice = workingSlice;
1117    tmpList << "# Disk Setup for " + workingDisk ;
1118
1119    // Check if this is an install to "Unused Space"
1120    for (int z=0; z < sysDisks.count(); ++z)
1121      if ( sysDisks.at(z).at(0) == "SLICE" \
1122        && sysDisks.at(z).at(2) == workingDisk + workingSlice \
1123        && sysDisks.at(z).at(4) == "Unused Space" )
1124          tmpSlice = "free";
1125
1126    tmpList << "disk" + tmp.setNum(disk) + "=" + workingDisk;
1127    tmpList << "partition=" + tmpSlice;
1128
1129    // Which boot-loader are we stamping?
1130    tmpList << "bootManager=" + bootLoader;
1131
1132    // Set the GPT/MBR options
1133    if ( loadGPT ) 
1134      tmpList << "partscheme=GPT";
1135    else
1136      tmpList << "partscheme=MBR";
1137
1138    tmpList << "commitDiskPart";
1139    tmpList << "";
1140
1141    // If after doing the mirror, our list is empty, break out
1142    if ( copyList.empty() )
1143      break;
1144   
1145    // Now print the partition section for this slice
1146    tmpList << "# Partition Setup for " + workingDisk + "(" + workingSlice + ")";
1147    tmpList << "# All sizes are expressed in MB";
1148    tmpList << "# Avail FS Types, UFS, UFS+S, UFS+SUJ, UFS+J, ZFS, SWAP";
1149    tmpList << "# UFS.eli, UFS+S.eli, UFS+SUJ, UFS+J.eli, ZFS.eli, SWAP.eli";
1150
1151    // If there is a dedicated /boot partition, need to list that first, see what is found
1152    for (int i=0; i < copyList.count(); ++i) {
1153      QStringList mounts = copyList.at(i).at(2).split(",");
1154      for (int z = 0; z < mounts.size(); ++z) {
1155        if ( copyList.at(i).at(0) == workingDisk \
1156          && copyList.at(i).at(1) == workingSlice \
1157          && mounts.at(z) == "/boot" )
1158                startPart="/boot";
1159      }
1160    }
1161
1162    // If no dedicated /boot partition, then lets list "/" first
1163    if(startPart.isEmpty())
1164        startPart="/";
1165
1166    // Start by looking for the root partition
1167    for (int i=0; i < copyList.count(); ++i) {
1168      QStringList mounts = copyList.at(i).at(2).split(",");
1169      for (int z = 0; z < mounts.size(); ++z) {
1170        if ( copyList.at(i).at(0) == workingDisk \
1171          && copyList.at(i).at(1) == workingSlice \
1172          && ( mounts.at(z) == startPart || mounts.at(z).indexOf(startPart + "(") == 0 ) ) {
1173
1174          // Check if we have any extra arguments to throw on the end
1175          XtraTmp="";
1176          if ( ! copyList.at(i).at(5).isEmpty() )
1177            XtraTmp=" (" + copyList.at(i).at(5) + ")" ;
1178
1179          // Write out the partition line
1180          tmpList << "disk" + tmp.setNum(disk) + "-part=" \
1181                   + copyList.at(i).at(3) + " " + copyList.at(i).at(4) \
1182                   + " " + copyList.at(i).at(2) + XtraTmp;
1183
1184          // Check if we have an encryption passphrase to use
1185          if ( ! copyList.at(i).at(6).isEmpty() )
1186            tmpList << "encpass=" + copyList.at(i).at(6);
1187
1188          // Done with this item, remove it now
1189          copyList.removeAt(i);
1190          break;
1191        }
1192      }
1193    }
1194
1195    // Now look for SWAP
1196    for (int i=0; i < copyList.count(); ++i) {
1197      if ( copyList.at(i).at(0) == workingDisk \
1198        && copyList.at(i).at(1) == workingSlice \
1199        && copyList.at(i).at(2) == "SWAP" ) {
1200
1201        // Write the partition line
1202        tmpList << "disk" + tmp.setNum(disk) + "-part=" \
1203                 + copyList.at(i).at(3) + " " + copyList.at(i).at(4) \
1204                 + " none";
1205
1206        // Done with this item, remove it now
1207        copyList.removeAt(i);
1208        break;
1209      }
1210    }
1211 
1212    // Now look for any other partitions
1213    int count = copyList.count();
1214    for (int i=0; i < count; ++i) {
1215      if ( copyList.at(i).at(0) == workingDisk \
1216        && copyList.at(i).at(1) == workingSlice ) {
1217
1218        // Check if we have any extra arguments to throw on the end
1219        XtraTmp="";
1220        if ( ! copyList.at(i).at(5).isEmpty() )
1221          XtraTmp=" (" + copyList.at(i).at(5) + ")" ;
1222
1223        // If we are working on the last partition, set the size to 0 to use remaining disk
1224        if ( i == (count - 1) ) 
1225                sliceSize = "0";
1226        else
1227                sliceSize=copyList.at(i).at(4);
1228
1229        // Write the partition line
1230        tmpList << "disk" + tmp.setNum(disk) + "-part=" \
1231                 + copyList.at(i).at(3) + " " + sliceSize \
1232                 + " " + copyList.at(i).at(2) + XtraTmp;
1233
1234        // Check if we have an encryption passphrase to use
1235        if ( ! copyList.at(i).at(6).isEmpty() )
1236          tmpList << "encpass=" + copyList.at(i).at(6);
1237
1238        // Done with this item, remove it now
1239        copyList.removeAt(i);
1240        i--;
1241        count--;
1242      }
1243    }
1244
1245
1246    // Close out this partition section
1247    tmpList << "commitDiskLabel";
1248    tmpList << "";
1249
1250    // Increment our disk counter
1251    disk++;
1252  }
1253
1254  return tmpList;
1255}
1256
1257// Slot which checks any disk requirements before procceding to the next page
1258bool Installer::checkDiskRequirements()
1259{
1260  // For now just return true, the wizard should handle making sure
1261  // the user doesn't shoot themselves in the foot during disk setup
1262  return true;
1263}
1264
1265// Function which begins the backend install, and connects slots to monitor it
1266void Installer::startInstall()
1267{
1268
1269  // Update the UI elements if doing a restore
1270  if ( radioRestore->isChecked() )
1271  {
1272      labelFinished->setText(tr("Your system is now restored!\nClick Finish to reboot. After rebooting you may eject the install media."));
1273      groupInstall->setTitle(tr("System Restore"));
1274      labelInstallHeader->setText(tr("Your system is now being restored, this may take a while depending upon the size of your backup and network conditions."));
1275  }
1276
1277  QString cfgFile;
1278  if (customCfgFile.isEmpty() )
1279    cfgFile = PCSYSINSTALLCFG;
1280  else
1281    cfgFile = customCfgFile;
1282
1283  // Disable the back / next buttons until we are finished
1284  nextButton->setEnabled(false);
1285  backButton->setEnabled(false);
1286  progressBarInstall->setValue(0); 
1287  installFoundCounter = false;
1288  installFoundMetaCounter = false;
1289  installFoundFetchOutput = false;
1290  inZFSSend = false;
1291
1292  // Setup some defaults for the secondary progress bar
1293  progressBarInstall2->setValue(0); 
1294  progressBarInstall2->setHidden(true); 
1295  labelInstallStatus2->setText("");
1296  labelInstallStatus2->setHidden(true);
1297
1298  // Kill any hald instances, which causes failures to install when it
1299  // tries to mount our new partitions
1300  QProcess::execute("killall", QStringList() << "hald");
1301
1302  // Start our process to begin the install
1303  QString PCSYSINSTALL;
1304  if ( QFile::exists("/root/pc-sysinstall/pc-sysinstall") ) 
1305     PCSYSINSTALL = "/root/pc-sysinstall/pc-sysinstall";
1306  else 
1307     PCSYSINSTALL = "/usr/local/sbin/pc-sysinstall";
1308
1309  QString program = PCSYSINSTALL;
1310  QStringList arguments;
1311  arguments << "-c" << cfgFile;
1312
1313  installProc = new QProcess();
1314  installProc->setProcessChannelMode(QProcess::MergedChannels);
1315  connect(installProc,SIGNAL(finished( int, QProcess::ExitStatus)),this,SLOT(slotInstallProcFinished( int, QProcess::ExitStatus)));
1316  connect(installProc,SIGNAL(readyRead()),this,SLOT(slotReadInstallerOutput()));
1317  installProc->start(program, arguments);
1318
1319}
1320
1321// Function run when the install failed to prompt user for course of action
1322void Installer::installFailed()
1323{
1324   QString sysLog;
1325   labelInstallStatus->setText(tr("Failed!"));
1326
1327   QMessageBox msgBox;
1328   msgBox.setWindowTitle(tr("PC-BSD Installer"));
1329   msgBox.setIcon(QMessageBox::Critical);
1330   msgBox.setText(tr("The installer has encountered an error and has been halted."));
1331   msgBox.setInformativeText(tr("Do you want to generate an error report?"));
1332   msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes);
1333   msgBox.setDefaultButton(QMessageBox::Yes);
1334
1335   // If we have a log, show it in the detailed view button
1336   if ( QFile::exists("/tmp/.pc-sysinstall/pc-sysinstall.log") )
1337   {
1338     QFile logFile("/tmp/.pc-sysinstall/pc-sysinstall.log");
1339     if (logFile.open(QIODevice::ReadOnly | QIODevice::Text))
1340         while (!logFile.atEnd())
1341           sysLog = sysLog + logFile.readLine() + "\n";
1342     msgBox.setDetailedText(sysLog);
1343   }
1344   int ret = msgBox.exec();
1345
1346   switch (ret) {
1347   case QMessageBox::Yes:
1348       // Generate the error report
1349       Scripts::Backend::createErrorReport();
1350       break;
1351   case QMessageBox::No: // :)
1352       break;
1353   }
1354
1355   QMessageBox msgBox2;
1356   msgBox2.setWindowTitle(tr("PC-BSD Installer"));
1357   msgBox2.setIcon(QMessageBox::Critical);
1358   msgBox2.setText(tr("Restart the system now?") );
1359   msgBox2.setStandardButtons(QMessageBox::No | QMessageBox::Yes);
1360   msgBox2.setDefaultButton(QMessageBox::Yes);
1361   msgBox2.setDetailedText(sysLog);
1362
1363   ret = msgBox2.exec();
1364
1365   switch (ret) {
1366   case QMessageBox::Yes:
1367       close();
1368       break;
1369   case QMessageBox::No: // :)
1370       break;
1371   }
1372
1373}
1374
1375// Slot which is called when the installation has finished
1376void Installer::slotInstallProcFinished( int exitCode, QProcess::ExitStatus status)
1377{
1378  QString tmp;
1379  if ( status != QProcess::NormalExit || exitCode != 0 )
1380  {
1381     installFailed();
1382  } else {
1383    // Move to the final page, and show a finish button
1384    proceed(true);
1385    nextButton->setEnabled(true);
1386    nextButton->setText(tr("&Finish"));
1387    nextButton->disconnect();
1388    connect(nextButton, SIGNAL(clicked()), this, SLOT(slotFinished()));
1389    backButton->setEnabled(false);
1390    abortButton->setEnabled(false);
1391  }
1392}
1393
1394// Slot which reads the output of the installer
1395void Installer::slotReadInstallerOutput()
1396{
1397  QString tmp, line;
1398  int range;
1399  bool ok;
1400
1401
1402  while ( installProc->canReadLine() )
1403  {
1404     tmp = installProc->readLine();
1405     tmp.truncate(75);
1406     //qDebug() << tmp;
1407
1408     // If doing a restore we can do all parsing right here
1409     if ( radioRestore->isChecked() ) {
1410       line = tmp;
1411
1412       if ( tmp.contains("total estimated size"))
1413       { 
1414          repTotalK = line.section(" ",-1).simplified();
1415          double totSize = displayToDoubleK(repTotalK);
1416          progressBarInstall->setRange(0, totSize + 1024);
1417          inZFSSend = true;
1418          continue;
1419       } 
1420       if ( tmp.contains("Moving datasets to"))
1421          inZFSSend=false;
1422
1423       if( line.contains("send from ") )
1424         continue;
1425       if( line.contains("TIME ") )
1426         continue;
1427
1428       labelInstallStatus->setText(tmp);
1429
1430       if ( ! inZFSSend )
1431          continue;
1432
1433       // We got here, lets parse the ZFS send message
1434       parseStatusMessage(tmp);
1435
1436       continue;
1437     } // End of restore parsing
1438
1439     // Parse fetch output
1440     if ( installFoundFetchOutput ) {
1441       if ( tmp.indexOf("SIZE: ") != -1 ) {
1442
1443          // Get the total range first
1444          line = tmp;
1445          tmp = tmp.remove(0, tmp.indexOf(":") + 2 );
1446          tmp.truncate(tmp.indexOf(" ")); 
1447          range = tmp.toInt(&ok);
1448          if ( ok )
1449             progressBarInstall->setRange(0, range + 1);
1450
1451          // Now get the current progress
1452          tmp = line;
1453          tmp = tmp.remove(0, tmp.indexOf(":") + 2 );
1454          tmp = tmp.remove(0, tmp.indexOf(":") + 2 );
1455          range = tmp.toInt(&ok);
1456          if ( ok )
1457             progressBarInstall->setValue(range);
1458           
1459          continue;
1460        } else {
1461          installFoundFetchOutput = false;
1462          break;
1463        }
1464     } 
1465
1466     // Unknown point in install
1467     if ( ! installFoundCounter && ! installFoundMetaCounter ) {
1468
1469        // Check if we've found fetch output to update the progress bar with
1470        if ( tmp.indexOf("FETCH: ") != -1 ) {
1471          installFoundFetchOutput = true;
1472          break;
1473        }
1474
1475        if ( tmp.indexOf("INSTALLCOUNT: ") != -1 ) {
1476          tmp = tmp.remove(0, tmp.indexOf(":") + 1 ); 
1477          range = tmp.toInt(&ok);
1478          if ( ok ) {
1479             range = range + 50;
1480             progressBarInstall->setRange(0, range + 1); 
1481             installFoundCounter = true;
1482             if ( availDesktopPackageData )
1483                labelInstallStatus->setText(tr("Extracting system...")); 
1484             else
1485                labelInstallStatus->setText(tr("Installing system... This may take a while...")); 
1486          }
1487
1488          break;
1489
1490        } 
1491
1492        // Check if we are on the meta-pkg installation
1493        if ( tmp.indexOf("Packages to install: ") != -1 ) {
1494          tmp = tmp.remove(0, tmp.indexOf(":") + 1 ); 
1495          range = tmp.toInt(&ok);
1496          if ( ok ) {
1497             progressBarInstall->setRange(0, range + 1); 
1498             progressBarInstall->setValue(0); 
1499             progressBarInstall2->setRange(0, 0); 
1500             labelInstallStatus2->setHidden(false);
1501             progressBarInstall2->setHidden(false); 
1502             installFoundMetaCounter = true;
1503             installFoundCounter = false;
1504             labelInstallStatus->setText(tr("Installing packages... This may take a while...")); 
1505             continue;
1506          }
1507
1508        } 
1509
1510        labelInstallStatus->setText(tmp);
1511        continue; 
1512     } 
1513
1514     // Doing file-extraction still
1515     if ( installFoundCounter ) {
1516
1517       // Doing dist-files, may have multiple images to extract
1518       if ( tmp.indexOf("INSTALLCOUNT: ") != -1 ) {
1519         tmp = tmp.remove(0, tmp.indexOf(":") + 1 ); 
1520         range = tmp.toInt(&ok);
1521         if ( ok ) {
1522            progressBarInstall->setRange(0, range + 1); 
1523            installFoundCounter = true;
1524            if ( availDesktopPackageData )
1525              labelInstallStatus->setText(tr("Extracting system...")); 
1526            else
1527              labelInstallStatus->setText(tr("Installing system... This may take a while...")); 
1528         }
1529         break;
1530       } 
1531
1532
1533       // Increment the progress
1534       progressBarInstall->setValue(progressBarInstall->value() + 1); 
1535
1536       // We've reached the end of this counted section
1537       if ( tmp.indexOf("Extraction Finished") != -1 ) {
1538         installFoundCounter = false;
1539         progressBarInstall->setRange(0, 0); 
1540       }
1541
1542       continue;
1543     }
1544
1545     // Doing meta-pkgs
1546     if ( installFoundMetaCounter ) {
1547        // Check if we are on the next meta-pkg
1548        if ( tmp.indexOf("Installing package: ") != -1 ) {
1549           progressBarInstall->setValue(progressBarInstall->value() + 1); 
1550           labelInstallStatus->setText(tr("Installing meta-package: %1").arg(tmp.section(":", 1, 5))); 
1551           continue;
1552        }
1553
1554        if ( tmp.indexOf("Package installation complete!") != -1 ) {
1555           installFoundMetaCounter = false;
1556           progressBarInstall->setRange(0, 0); 
1557           progressBarInstall2->setHidden(true);
1558           labelInstallStatus2->setHidden(true);
1559           continue;
1560        }
1561
1562        // If we are installing a package, show details to user
1563        if ( tmp.indexOf("Installing") == 0 )
1564          labelInstallStatus2->setText(tmp.section("...", 0, 0));
1565     }
1566
1567  } // end of while loop
1568}
1569
1570// Return list of components to install
1571QStringList Installer::getComponentCfgSettings()
1572{
1573  QStringList componentList, com;
1574  if ( fSRC )
1575    com << "src";
1576  if ( fPORTS )
1577    com << "ports";
1578
1579  if ( ! com.isEmpty() ) {
1580    componentList << "";
1581    componentList << "# Optional Components";
1582    componentList << "installComponents=" + com.join(",");
1583  }
1584
1585  return componentList;
1586}
1587
1588// Start xvkbd
1589void Installer::slotPushVirtKeyboard()
1590{
1591   system("killall -9 xvkbd; xvkbd -compact &");
1592}
1593
1594// Return the configuration for desktop packages
1595QStringList Installer::getDeskPkgCfg()
1596{
1597   QStringList cfgList, pkgList;
1598   QString line;
1599
1600   QList<QStringList> curList;
1601
1602   if ( radioDesktop->isChecked() ) {
1603     curList = listDeskPkgs;
1604     pkgList << "pcbsd-base";
1605   } else {
1606     curList = listServerPkgs;
1607     pkgList << "trueos-base";
1608   }
1609
1610   // Loop though list of pkgs, see what to install
1611   for ( int d=0; d < curList.count(); ++d) {
1612     for ( int i=0; i < selectedPkgs.count(); ++i)
1613        // Is the package selected or the base-system?
1614        if ( curList.at(d).at(0) == selectedPkgs.at(i) || curList.at(d).at(0) == "base-system" ) {
1615
1616           // Yay! Lets get a list of packages to install
1617           QFile mFile;
1618           mFile.setFileName(curList.at(d).at(6));
1619           if ( ! mFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
1620              qDebug() << "Invalid meta-pkg list:" << curList.at(d).at(6);
1621              break;
1622           }
1623 
1624           // Read in the meta pkg list
1625           QTextStream in(&mFile);
1626           while ( !in.atEnd() ) {
1627             line = in.readLine().simplified();
1628             if ( line.isEmpty() )
1629                 continue; 
1630             
1631             pkgList << line.section(":", 0,0);
1632           }
1633           mFile.close();
1634           break;
1635        }
1636   }
1637
1638   cfgList << "installPackages=" + pkgList.join(" ");
1639   return cfgList;
1640}
1641
1642// Return list of users in cfg format
1643QStringList Installer::getUsersCfgSettings()
1644{
1645   QStringList userList;
1646 
1647   userList << "";
1648   userList << "# Root Password";
1649   userList << "rootPass=" + fRootPW;
1650
1651   userList << "";
1652   userList << "# Users";
1653   userList << "userName=" + fUserName;
1654   userList << "userComment=" + fName;
1655   userList << "userPass=" + fUserPW;
1656   userList << "userShell=" + fShell;
1657   userList << "userHome=/home/" + fUserName;
1658   userList << "userGroups=wheel,operator";
1659   userList << "commitUser";
1660   userList << "";
1661 
1662   return userList;
1663}
1664
1665void Installer::closeEvent(QCloseEvent *event)
1666{
1667    int ret = QMessageBox::question(this, tr("PC-BSD Installer"),
1668                                tr("Are you sure you want to abort this installation?"),
1669                                QMessageBox::No | QMessageBox::Yes,
1670                                QMessageBox::No);
1671    switch (ret) {
1672    case QMessageBox::Yes:
1673        //exit the installer :(
1674        break;
1675    case QMessageBox::No: // :)
1676        event->ignore();
1677        break;
1678    }
1679}
1680
1681void Installer::checkSpaceWarning()
1682{
1683  int totalSize = -1;
1684  int targetSize;
1685  int targetLoc;
1686  bool ok;
1687  QString workingDisk = sysFinalDiskLayout.at(0).at(0);
1688  QString workingSlice = sysFinalDiskLayout.at(0).at(1);
1689  QString targetType;
1690  QString target;
1691  //qDebug() << "Disk layout:" << workingDisk << workingSlice;
1692
1693  if ( workingSlice == "ALL" ) {
1694    targetType = "DRIVE";
1695    target = workingDisk;
1696    targetLoc = 1;
1697  } else {
1698    targetType = "SLICE";
1699    target = workingDisk + workingSlice;
1700    targetLoc = 2;
1701  }
1702 
1703  // Lets get the size for this disk / partition
1704  for (int i=0; i < sysDisks.count(); ++i) {
1705      // Make sure to only add the slices to the listDiskSlices
1706      if ( sysDisks.at(i).at(0) == targetType && target == sysDisks.at(i).at(targetLoc))
1707        totalSize = sysDisks.at(i).at(targetLoc + 1).toInt(&ok);
1708  }
1709
1710  //qDebug() << totalSize;
1711
1712  if ( radioServer->isChecked() )
1713     targetSize=20000;
1714  else
1715     targetSize=50000;
1716
1717  int tGB = targetSize / 1000;
1718
1719  qDebug() << totalSize << targetSize;
1720
1721  // Lets print a nice handy warning for users with possible
1722  // low disk space issues
1723  if ( totalSize < targetSize ) {
1724  QMessageBox::warning(this, tr("PC-BSD Installer"),
1725      QString(tr("The selected disk / partition is less than recommended %1GB. The installation may fail...")).arg(tGB),
1726      QMessageBox::Ok,
1727      QMessageBox::Ok);
1728      haveWarnedSpace = true;     
1729  }
1730
1731  return;
1732}
1733
1734void Installer::slotSaveConfigUSB()
1735{
1736  int ret = QMessageBox::question(this, tr("PC-BSD Installer"),
1737           tr("This will save your installation configuration to a MSDOSFS/FAT32 formatted USB stick. Continue?"),
1738           QMessageBox::No | QMessageBox::Yes,
1739           QMessageBox::No);
1740  switch (ret) {
1741  case QMessageBox::Yes:
1742      break;
1743  case QMessageBox::No: // :)
1744      return;
1745      break;
1746  }
1747
1748  // Prompt to insert USB stick
1749  QMessageBox::information(this, tr("PC-BSD Installer"),
1750          tr("Please insert a USB stick now, and click OK to continue."),
1751          QMessageBox::Ok,
1752          QMessageBox::Ok);
1753
1754  // While USB is settling, this is a good time to ask for the config nickname
1755  bool ok;
1756  QString cfgName = QInputDialog::getText(this, tr("PC-BSD Installer"),
1757                 tr("Please enter the nickname you want to save this configuration as."), 
1758                 QLineEdit::Normal,
1759                 QString("default"), &ok);
1760  if (!ok || cfgName.isEmpty())
1761     return;
1762
1763  // Now lets try to save the media
1764  qDebug() << "Running: /root/save-to-usb.sh" << cfgName;
1765  QProcess m;
1766  m.start(QString("/root/save-to-usb.sh"), QStringList() << cfgName);
1767  while(m.state() == QProcess::Starting || m.state() == QProcess::Running) {
1768     m.waitForFinished(200);
1769     QCoreApplication::processEvents();
1770  }
1771
1772  if ( m.exitCode() != 0 ) {
1773     QMessageBox::critical(this, tr("PC-BSD Installer"),
1774          tr("Failed saving config to USB media. Is the device working and formatted MSDOSFS/FAT32?"),
1775          QMessageBox::Ok,
1776          QMessageBox::Ok);
1777
1778  } else {
1779     QMessageBox::information(this, tr("PC-BSD Installer"),
1780          tr("Configuration saved! You may now safely remove the USB media."),
1781          QMessageBox::Ok,
1782          QMessageBox::Ok);
1783  }
1784 
1785}
1786
1787void Installer::slotLoadConfigUSB()
1788{
1789  int ret = QMessageBox::question(this, tr("PC-BSD Installer"),
1790           tr("This will load a installation configuration file your MSDOSFS/FAT32 formatted USB stick. Continue?"),
1791           QMessageBox::No | QMessageBox::Yes,
1792           QMessageBox::No);
1793  switch (ret) {
1794  case QMessageBox::Yes:
1795      break;
1796  case QMessageBox::No: // :)
1797      return;
1798      break;
1799  }
1800
1801  // Prompt to insert USB stick
1802  QMessageBox::information(this, tr("PC-BSD Installer"),
1803          tr("Please insert the USB stick now, and click OK to continue."),
1804          QMessageBox::Ok,
1805          QMessageBox::Ok);
1806
1807  // Now lets try to load the config files
1808  qDebug() << "Running: /root/get-from-usb.sh";
1809  QProcess m;
1810  m.start(QString("/root/get-from-usb.sh"), QStringList());
1811  while(m.state() == QProcess::Starting || m.state() == QProcess::Running) {
1812     m.waitForFinished(200);
1813     QCoreApplication::processEvents();
1814  }
1815
1816  if ( m.exitCode() != 0 ) {
1817     QMessageBox::critical(this, tr("PC-BSD Installer"),
1818          tr("Failed loading saved config file from the USB media. Is the device working and formatted MSDOSFS/FAT32?"),
1819          QMessageBox::Ok,
1820          QMessageBox::Ok);
1821     return;
1822
1823  } else {
1824     QMessageBox::information(this, tr("PC-BSD Installer"),
1825          tr("Configuration loaded! You may now safely remove the USB media."),
1826          QMessageBox::Ok,
1827          QMessageBox::Ok);
1828  }
1829
1830 
1831  // Yay! Now lets prompt the user as to which config to use
1832  QDir cDir;
1833  cDir.setPath("/tmp/pc-sys");
1834  QStringList cfgs = cDir.entryList(QDir::Files);
1835
1836  bool ok;
1837  QString cfgFile = QInputDialog::getItem(this, tr("PC-BSD Installer"),
1838                                       tr("Config File:"), cfgs, 0, false, &ok);
1839  if (!ok || cfgFile.isEmpty())
1840    return;
1841 
1842  ret = QMessageBox::question(this, tr("PC-BSD Installer"),
1843           tr("Start the install using this config file?") + "\n" + cfgFile,
1844           QMessageBox::No | QMessageBox::Yes,
1845           QMessageBox::No);
1846  switch (ret) {
1847  case QMessageBox::Yes:
1848      break;
1849  case QMessageBox::No: // :)
1850      return;
1851      break;
1852  }
1853
1854  // Time to start the installation with our custom config file
1855  qDebug() << cfgFile;
1856  customCfgFile = "/tmp/pc-sys/" + cfgFile;
1857  installStackWidget->setCurrentIndex(3);
1858  startInstall();
1859}
1860
1861void Installer::slotStartNetworkManager() 
1862{
1863  system("/usr/local/bin/pc-netmanager -installer &");
1864}
1865
1866void Installer::slotSaveRestoreSettings(QStringList Opts)
1867{
1868  restOpts = Opts;
1869
1870  textEditDiskSummary->clear();
1871  QStringList summary = getDiskSummary();
1872  for ( int i=0; i < summary.count(); ++i)
1873    textEditDiskSummary->append(summary.at(i));
1874               
1875  textEditDiskSummary->moveCursor(QTextCursor::Start);
1876 
1877  startConfigGen();
1878  installStackWidget->setCurrentIndex(installStackWidget->currentIndex() + 1);
1879}
1880
1881void Installer::parseStatusMessage(QString stat){
1882  //qDebug() << "msg:" << stat;
1883  QString dispTotal;
1884
1885  //Divide up the status message into sections
1886  stat.replace("\t"," ");
1887  QString cSize = stat.section(" ",1,1,QString::SectionSkipEmpty);
1888
1889  //Now Setup the tooltip
1890  if(!repTotalK.isEmpty()){
1891    double c = displayToDoubleK(cSize);
1892    progressBarInstall->setValue(c);
1893    dispTotal = repTotalK;
1894  } else {
1895    dispTotal = "??";
1896  }
1897
1898  //Format the tooltip String
1899  QString status = cSize+"/"+dispTotal;
1900
1901  QString txt = QString(tr("Restoring system: %1")).arg(status);
1902  labelInstallStatus->setText(txt);
1903
1904}
1905
1906double Installer::displayToDoubleK(QString displayNumber){
1907  QStringList labels;
1908    labels << "K" << "M" << "G" << "T" << "P" << "E";
1909  QString clab = displayNumber.right(1); //last character is the size label
1910        displayNumber.chop(1); //remove the label from the number
1911  double num = displayNumber.toDouble();
1912  //Now format the number properly
1913  bool ok = false;
1914  for(int i=0; i<labels.length(); i++){
1915    if(labels[i] == clab){ ok = true; break; }
1916    else{ num = num*1024; } //get ready for the next size
1917  }
1918  if(!ok){ num = -1; } //could not determine the size
1919  return num;
1920}
1921
Note: See TracBrowser for help on using the repository browser.