1
0
Fork 0
caffyBoot/BerrybootGUI2.0/bootmenudialog.cpp

932 lines
25 KiB
C++

/* Berryboot -- boot menu dialog
*
* Copyright (c) 2012, Floris Bos
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "bootmenudialog.h"
#include "ui_bootmenudialog.h"
#include "installer.h"
#include "localedialog.h"
#include "diskdialog.h"
#include "adddialog.h"
#include "mainwindow.h"
#include <iostream>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/reboot.h>
#include <QDir>
#include <QFile>
#include <QProgressDialog>
#include <QProcess>
#include <QMessageBox>
#include <QDesktopWidget>
#include <QTime>
#include <QDebug>
#include <QCloseEvent>
#include <QScreen>
#define runonce_file "/mnt/data/runonce"
#define default_file "/mnt/data/default"
BootMenuDialog::BootMenuDialog(Installer *i, QWidget *parent) :
QDialog(parent),
ui(new Ui::BootMenuDialog),
_i(i),
_countdown(11)
{
ui->setupUi(this);
#ifdef Q_WS_QWS
/* Make items twice as large if using the touch screen */
QScreen *scr = QScreen::instance();
if (scr->height() == 480)
{
QFont font = ui->list->font();
font.setPointSize(24);
ui->list->setFont(font);
resize(600, height());
}
#endif
setEnabled(false);
QTimer::singleShot(1, this, SLOT(initialize()));
}
BootMenuDialog::~BootMenuDialog()
{
stopSSHserver();
delete ui;
}
void BootMenuDialog::closeEvent(QCloseEvent *ev)
{
ev->ignore();
}
/* Mount data partition and populate menu
* TODO: move stuff to seperate thread instead of messing with processEvents()
*/
void BootMenuDialog::initialize()
{
bool success = false;
QByteArray datadev = _i->bootParam("datadev");
QByteArray qmap = _i->bootParam("qmap");
if (!qmap.isEmpty())
_i->setKeyboardLayout(qmap);
startSSHserverIfEnabled();
if (!datadev.isEmpty())
{
if (datadev == "iscsi")
{
startISCSI();
}
else
{
_i->startNetworkInterface();
}
}
else
{
initializeA10();
startInstaller();
return;
}
QProgressDialog qpd(tr("Waiting for data partition %1").arg(QString(datadev)), QString(), 0, 0, this);
qpd.show();
QApplication::processEvents();
/* Wait 10 seconds for named data partition */
waitForDevice(datadev);
qpd.setLabelText(tr("Mounting data partition %1").arg(QString(datadev)));
QApplication::processEvents();
if (_i->bootoptions().contains("luks"))
{
askLuksPassword(datadev);
success = mountDataPartition("mapper/luks");
}
else
{
success = mountDataPartition(datadev);
}
if (!success)
{
qpd.setLabelText(tr("Data is not at %1. Searching other partitions...").arg(QString(datadev)));
QApplication::processEvents();
datadev.clear();
/* Search 10 times */
for (unsigned int i=0; i<10 && datadev.isEmpty(); i++)
{
if (i != 0)
{
processEventSleep(1000);
}
datadev = getPartitionByLabel();
}
if (!datadev.isEmpty())
{
qpd.setLabelText(tr("Found other berryboot partition to mount: %1").arg(QString(datadev)));
QApplication::processEvents();
success = mountDataPartition(datadev);
}
}
if (!success)
{
qpd.hide();
datadev = _i->bootParam("datadev");
if (_i->bootoptions().contains("luks"))
datadev = "mapper/luks";
if (QFile::exists("/dev/"+datadev) && !_i->bootoptions().contains("btrfs"))
{
if (QMessageBox::question(this, tr("Perform fsck?"),
tr("Error mounting data partition. Try to repair file system?"), QMessageBox::Yes, QMessageBox::No)
== QMessageBox::Yes)
{
QProcess proc;
_i->switchConsole(5);
proc.start(QByteArray("openvt -c 5 -w /usr/sbin/fsck.ext4 -yf /dev/"+datadev));
QApplication::processEvents();
proc.waitForFinished(-1);
success = mountDataPartition(datadev);
_i->switchConsole(1);
}
}
if (!success)
{
QMessageBox::critical(this, tr("No data found..."), tr("Cannot find my data partition :-("), QMessageBox::Ok);
reject();
return;
}
}
if (QFile::exists("/sbin/udevd"))
_i->loadDrivers();
initializeA10();
if (QFile::exists(runonce_file))
{
waitForRemountRW();
qpd.setLabelText(tr("Removing runonce file"));
QApplication::processEvents();
QByteArray runonce = file_get_contents(runonce_file);
QFile::remove(runonce_file);
sync();
file_put_contents("/tmp/answer", runonce);
reject();
return;
}
qpd.hide();
if (_i->bootoptions().contains("reconfigure"))
{
reconfigureLocale();
}
QString def = _i->getDefaultImage();
QMap<QString,QString> images = _i->listInstalledImages();
if (images.isEmpty())
{
startInstaller();
return;
}
if (def.isEmpty() )
def = images.begin().key();
for (QMap<QString,QString>::const_iterator iter = images.constBegin(); iter != images.constEnd(); iter++)
{
QListWidgetItem *item = new QListWidgetItem(QIcon(), iter.value(), ui->list);
item->setData(Qt::UserRole, iter.key());
if (def == iter.key())
ui->list->setCurrentItem(item);
}
if (_i->bootoptions().contains("sound"))
{
/* Set sound channel (HDMI/headphones) */
_i->loadSoundModule(_i->bootParam("sound"));
}
if (!_i->bootoptions().contains("nobootmenutimeout"))
{
// start timer
QByteArray bootmenutimeout = _i->bootParam("bootmenutimeout");
if (!bootmenutimeout.isEmpty())
{
_countdown = bootmenutimeout.toInt()+1;
}
autoBootTimeout();
connect(&_countdownTimer, SIGNAL(timeout()), this, SLOT(autoBootTimeout()));
_countdownTimer.setInterval(1000);
_countdownTimer.start();
ui->list->installEventFilter(this);
connect(ui->list, SIGNAL(itemPressed(QListWidgetItem*)), this, SLOT(stopCountdown()));
}
setEnabled(true);
ui->list->setFocus();
}
void BootMenuDialog::on_bootButton_clicked()
{
bootImage(ui->list->currentItem()->data(Qt::UserRole).toString());
}
void BootMenuDialog::on_settingsButton_clicked()
{
stopCountdown();
startInstaller();
}
void BootMenuDialog::on_poweroffButton_clicked()
{
halt();
}
void BootMenuDialog::on_list_activated(const QModelIndex &)
{
on_bootButton_clicked();
}
void BootMenuDialog::autoBootTimeout()
{
ui->bootButton->setText(tr("Boot (%1) ").arg(QString::number(--_countdown)));
if (!_countdown)
{
_countdownTimer.stop();
on_bootButton_clicked();
}
}
/* Stop autoboot countdown on any key press or mouse button click */
bool BootMenuDialog::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress || event->type() == QEvent::MouseButtonPress)
{
stopCountdown();
if (obj == ui->list)
{
ui->list->removeEventFilter(this);
return false;
}
}
return QDialog::eventFilter(obj, event);
}
void BootMenuDialog::stopCountdown()
{
if (_countdownTimer.isActive())
{
_countdownTimer.stop();
ui->bootButton->setText(tr("Boot "));
}
}
void BootMenuDialog::bootImage(const QString &name)
{
int currentmemsplit = currentMemsplit();
int needsmemsplit = imageNeedsMemsplit(name);
waitForRemountRW();
if (_i->isMemsplitHandlingEnabled() && needsmemsplit && needsmemsplit != currentmemsplit)
{
mountSystemPartition();
QProgressDialog qpd(tr("Changing memsplit and rebooting..."), QString(), 0, 0, this);
qpd.show();
QApplication::processEvents();
/* Edit config.txt */
QByteArray newconfig, oldconfig = file_get_contents("/boot/config.txt");
QList<QByteArray> lines = oldconfig.split('\n');
/* Copy all lines except the old gpu_mem= parameter to the new config.txt */
foreach (QByteArray line, lines)
{
if (!line.startsWith("gpu_mem"))
newconfig += line + "\n";
}
newconfig = newconfig.trimmed()+"\n";
newconfig += memsplitParameter(needsmemsplit);
/* write new config.txt to temporary file first */
file_put_contents("/boot/config.new", newconfig);
/* sync() the data of the new file to disk */
sync();
/* rename() it. This is an atomic operation according to man page */
rename("/boot/config.new", "/boot/config.txt");
umountSystemPartition();
file_put_contents(runonce_file, name.toLatin1());
QProcess::execute("umount /mnt");
sync(); //sleep(1);
reboot();
}
else
{
file_put_contents("/tmp/answer", name.toLatin1() );
reject();
}
}
bool BootMenuDialog::memsplitsEnabled()
{
/* CCM for the win */
return false;
}
void BootMenuDialog::startInstaller()
{
_i->startNetworking();
mountSystemPartition();
waitForRemountRW();
accept();
}
void BootMenuDialog::reconfigureLocale()
{
_i->startNetworking();
mountSystemPartition();
waitForRemountRW();
LocaleDialog ld(_i);
if (ld.exec() == ld.Accepted)
{
QByteArray param;
/* Static MAC setting */
if (_i->fixateMAC())
{
QByteArray mac = _i->macAddress();
if (!mac.isEmpty())
param += " mac_addr="+mac;
}
/* Sound channel selection (hdmi audio/headphones) */
if (!_i->sound().isEmpty())
{
param += " sound="+_i->sound();
}
QByteArray qmap = _i->keyboardlayout().toLatin1();
if (!qmap.isEmpty() && qmap != "us")
param += " qmap="+qmap;
QFile f("/boot/cmdline.txt");
f.open(QIODevice::ReadOnly);
QByteArray line = f.readAll().trimmed();
line.replace(" reconfigure", "");
QByteArray cmdlinetxt = line+param;
f.close();
f.open(QIODevice::WriteOnly);
f.write(cmdlinetxt);
f.close();
/* Overscan setting */
bool configchanged = false;
f.setFileName("/boot/config.txt");
f.open(QIODevice::ReadOnly);
QByteArray configdata = f.readAll();
f.close();
bool overscanCurrentlyDisabled = configdata.contains("disable_overscan=1");
if (_i->disableOverscan() && !overscanCurrentlyDisabled)
{
configdata += "\ndisable_overscan=1";
configchanged = true;
}
else if (!_i->disableOverscan() && overscanCurrentlyDisabled)
{
configdata.replace("disable_overscan=1", "");
configchanged = true;
}
if (configchanged)
{
f.open(QIODevice::WriteOnly);
f.write(configdata.trimmed());
f.close();
}
if (QFile::exists("/boot/wpa_supplicant.conf"))
{
if (QFile::exists("/mnt/shared/etc/wpa_supplicant/wpa_supplicant.conf"))
{
QFile::remove("/mnt/shared/etc/wpa_supplicant/wpa_supplicant.conf");
}
else
{
QDir dir;
dir.mkdir("/mnt/shared/etc/wpa_supplicant");
}
QFile::copy("/boot/wpa_supplicant.conf", "/mnt/shared/etc/wpa_supplicant/wpa_supplicant.conf");
QFile::setPermissions("/mnt/shared/etc/wpa_supplicant/wpa_supplicant.conf", QFile::ReadOwner | QFile::WriteOwner);
}
}
umountSystemPartition();
}
void BootMenuDialog::startISCSI()
{
if (!QFile::exists("/sys/module/iscsi_tcp"))
{
loadModule("iscsi_tcp");
_i->loadDrivers();
}
QProgressDialog qpd(tr("Waiting for network to be ready"), QString(), 0, 0, this);
connect(_i, SIGNAL(networkInterfaceUp()), &qpd, SLOT(close()));
_i->startNetworking();
while (!_i->networkReady())
{
qpd.exec();
}
int tries = 1, delay = 5;
mountSystemPartition();
while (true)
{
QProcess proc;
connect(&proc, SIGNAL(finished(int)), &qpd, SLOT(close()));
qpd.setLabelText(tr("Connecting to iSCSI SAN"));
proc.start("sh /boot/iscsi.sh");
while (proc.state() != proc.NotRunning)
qpd.exec();
if (proc.exitCode() == 0)
break;
/* Wait a while and try again */
qpd.setLabelText(tr("Connecting to iSCSI SAN failed. Try # %1. Retrying in %2 seconds.")
.arg(QString::number(tries), QString::number(delay)));
processEventSleep(delay * 1000);
tries++; delay = qMin(delay * 2, 300);
}
umountSystemPartition();
/* Detect data partition */
QByteArray iscsiDevice = "/dev/"+_i->iscsiDevice().toLatin1();
QByteArray iscsiPart;
if (QFile::exists(iscsiDevice+"2"))
iscsiPart = iscsiDevice+"2";
else if (QFile::exists(iscsiDevice+"1"))
iscsiPart = iscsiDevice+"1";
else
QMessageBox::critical(this, tr("iSCSI error"), tr("iSCSI target does not have any partitions"), QMessageBox::Ok);
qDebug() << "iscsi partition:" << iscsiPart;
if (symlink(iscsiPart.constData(), "/dev/iscsi"))
{
// show error?
}
}
void BootMenuDialog::loadModule(const QByteArray &name)
{
QProgressDialog qpd(QString(), QString(), 0, 0, this);
if (!QFile::exists("/lib/modules"))
{
if (!QFile::exists("/mnt/shared"))
{
/* Mount boot partition, and uncompress /boot/shared.tgz to memory */
mountSystemPartition();
qpd.setLabelText(tr("Uncompressing drivers"));
qpd.show();
QApplication::processEvents();
_i->prepareDrivers();
umountSystemPartition();
}
else
{
/* Use modules from /mnt/shared on disk */
_i->prepareDrivers();
}
}
qpd.setLabelText(tr("Loading module: %1").arg(QString(name)));
qpd.show();
QApplication::processEvents();
QProcess::execute("/sbin/modprobe", QStringList(name));
}
void BootMenuDialog::initializeA10()
{
QByteArray cpuinfo = file_get_contents("/proc/cpuinfo");
if (cpuinfo.contains("sun4i") || cpuinfo.contains("sun5i"))
{
/* Some Allwinner A10/A13 drivers are not compiled into the kernel
load them as module
FIXME: driver should be loaded dynamically
*/
/*loadModule("ump");
loadModule("mali");
loadModule("mali_drm");*/
// Wifi
loadModule("8192cu");
}
/* If using a static wifi configuration, start it now */
if (_i->bootParam("ipv4").endsWith("/wlan0"))
{
mountSystemPartition();
_i->startWifi();
umountSystemPartition();
}
}
/* Utility functions copied from old BerrybootUI application */
QByteArray BootMenuDialog::file_get_contents(const QString &filename)
{
QFile f(filename);
f.open(f.ReadOnly);
QByteArray data = f.readAll();
f.close();
return data;
}
void BootMenuDialog::file_put_contents(const QString &filename, const QByteArray &data)
{
QFile f(filename);
f.open(f.WriteOnly);
f.write(data);
f.close();
}
QByteArray BootMenuDialog::getBootOptions()
{
return _i->bootoptions();
}
bool BootMenuDialog::mountDataPartition(const QString &dev, bool rw)
{
QString mountoptions = "-o noatime";
if (!rw)
mountoptions += ",ro";
if (!QFile::exists("/mnt"))
mkdir("/mnt", 0755);
if (getBootOptions().contains("fstype=btrfs"))
{
mountoptions += ",compress=lzo -t btrfs";
//loadModule("btrfs");
}
else
{
mountoptions += " -t ext4";
}
/* Try mounting read-only first.
* If that fails try mounting read-write straight away as it might recover from journal */
if (QProcess::execute("mount "+mountoptions+" /dev/"+dev+" /mnt") != 0
&& QProcess::execute("mount "+mountoptions.replace(",ro","")+" /dev/"+dev+" /mnt") != 0)
{
return false;
}
if (!QFile::exists("/mnt/images"))
{
if (QProcess::execute("umount /mnt") != 0)
{
//log_error("Error unmounting data partition");
}
return false;
}
/* Remount read-write in the background */
if (!rw)
_remountproc.start("mount -o remount,rw /mnt");
return true;
}
void BootMenuDialog::waitForRemountRW()
{
if (_remountproc.state() != _remountproc.NotRunning)
{
QProgressDialog qpd(tr("Remounting data partition read-write"), QString(), 0, 0, this);
qpd.show();
QApplication::processEvents();
if (_remountproc.state() != _remountproc.NotRunning && !_remountproc.waitForFinished())
{
QMessageBox::critical(this, tr("Error remounting data partition"), tr("Timed out waiting for remounting data partition read-write to complete"), QMessageBox::Ok);
}
}
if (_remountproc.exitCode() != 0)
{
if (QMessageBox::question(this, tr("Perform fsck?"),
tr("Error remounting data partition read-write. Try to repair file system?"), QMessageBox::Yes, QMessageBox::No)
== QMessageBox::Yes)
{
QByteArray datadev = _i->datadev().toLatin1();
if (_i->bootoptions().contains("luks"))
datadev = "mapper/luks";
qDebug() << "killing udev" << QProcess::execute("killall udevd");
qDebug() << "unmounting" << QProcess::execute("umount -f /dev/"+datadev);
QProcess proc;
_i->switchConsole(5);
proc.start(QByteArray("openvt -c 5 -w /usr/sbin/fsck.ext4 -yf /dev/"+datadev));
QApplication::processEvents();
proc.waitForFinished(-1);
mountDataPartition(datadev, true);
_i->switchConsole(1);
}
}
}
bool BootMenuDialog::waitForDevice(const QString &dev)
{
QTime t;
t.start();
while (t.elapsed() < 10000)
{
if (QFile::exists("/dev/"+dev))
return true;
QApplication::processEvents(QEventLoop::WaitForMoreEvents, 250);
}
return false;
}
QByteArray BootMenuDialog::getPartitionByLabel(const QString &label)
{
QByteArray dev;
QProcess proc;
proc.start("/sbin/findfs LABEL="+label);
if (proc.waitForFinished() && proc.exitCode() == 0)
{
dev = proc.readAll().trimmed();
if (dev.size()>5) /* Remove /dev and trailing \n */
dev = dev.mid(5);
}
return dev;
}
void BootMenuDialog::mountSystemPartition()
{
QProgressDialog qpd(tr("Mounting system partition..."), QString(), 0, 0, this);
qpd.show();
QApplication::processEvents();
if (!QFile::exists("/boot"))
mkdir("/boot", 0755);
if (_i->isPxeBoot())
return;
QString bootdev = _i->bootdev();
if (bootdev == "mmcblk0p1")
waitForDevice("mmcblk0");
else
waitForDevice(bootdev);
for (unsigned int tries=0; tries<2; tries++)
{
if (bootdev == "mmcblk0p1")
{
if (QProcess::execute("mount /dev/mmcblk0p1 /boot") == 0 || QProcess::execute("mount /dev/mmcblk0 /boot") == 0)
return;
}
else
{
if (QProcess::execute("mount /dev/"+bootdev+" /boot") == 0)
return;
}
qpd.setLabelText(tr("Error mounting system partition... Retrying..."));
processEventSleep(1000);
}
qpd.hide();
QMessageBox::critical(this, tr("Error mounting system partition"), tr("Unable to mount system partition"), QMessageBox::Ok);
}
void BootMenuDialog::umountSystemPartition()
{
if (_i->isPxeBoot())
return;
QProcess::execute("umount /boot");
}
int BootMenuDialog::currentMemsplit()
{
QByteArray meminfo = file_get_contents("/proc/meminfo");
// MemTotal: 3713768 kB
QByteArray memtotalstr = meminfo.mid(0, meminfo.indexOf(" kB"));
memtotalstr = memtotalstr.mid(memtotalstr.lastIndexOf(" ")+1);
int memtotal = memtotalstr.toInt();
if (memtotal > 495000) /* 512 MB model. Return corespondening 256 MB model values for compatibility */
return 240;
else if (memtotal > 479000)
return 224;
else if (memtotal > 447000)
return 192;
else if (memtotal > 255000)
return 128;
else if (memtotal > 249000)
return 256;
else if (memtotal > 230000) /* Original 256 MB model */
return 240;
else if (memtotal > 200000)
return 224;
else if (memtotal > 130000)
return 192;
else
return 128;
}
int BootMenuDialog::imageNeedsMemsplit(const QString &name)
{
/* image file names are like: berryterminal.img224 */
int imgpos = name.lastIndexOf(".img");
if (imgpos != -1 && imgpos+5 < name.size())
{
return name.mid(imgpos+4).toInt();
}
else
{
return 0;
}
}
QByteArray BootMenuDialog::memsplitParameter(int memsplit)
{
const char *r;
switch (memsplit)
{
case 240:
r = "gpu_mem=16\n";
break;
case 224:
r = "gpu_mem=32\n";
break;
case 128:
r = "gpu_mem=128\n";
break;
case 256:
r = "gpu_mem=128\ngpu_mem_512=256\n";
break;
default:
r = "gpu_mem=64\n";
}
return r;
}
void BootMenuDialog::reboot()
{
QProcess::execute("umount -ar");
sync();
::reboot(RB_AUTOBOOT);
}
void BootMenuDialog::halt()
{
QProcess::execute("killall udevd");
::usleep(100000);
QProcess::execute("umount -ar");
sync();
if (_i->datadev().startsWith("sda"))
{
/* Spin down drive */
QProcess::execute("/sbin/hdparm -Y /dev/sda");
}
::reboot(RB_POWER_OFF);
}
void BootMenuDialog::processEventSleep(int ms)
{
QTime t;
t.start();
while (t.elapsed() < ms)
{
int sleepfor = ms-t.elapsed();
if (sleepfor > 0)
QApplication::processEvents(QEventLoop::WaitForMoreEvents, sleepfor);
}
}
void BootMenuDialog::askLuksPassword(const QString &datadev)
{
loadModule("dm_crypt");
loadModule("aes");
loadModule("sha256");
loadModule("algif_hash");
/* For added security let cryptsetup ask for password in a text console,
* as it can remain in memory if we do it in the GUI.
* Also allow it to be entered through SSH if enabled.
*/
QProcess proc;
::mkdir("/etc/profile.d", 0755);
QFile f("/etc/profile.d/ssh-luks-prompt.sh");
f.open(f.WriteOnly);
f.write("/usr/sbin/cryptsetup luksOpen /dev/"+datadev.toLatin1()+" luks && killall openvt");
f.close();
_i->switchConsole(5);
while (!QFile::exists("/dev/mapper/luks"))
{
proc.start(QByteArray("openvt -c 5 -w /usr/sbin/cryptsetup luksOpen /dev/")+datadev+" luks");
QApplication::processEvents();
proc.waitForFinished();
}
_i->switchConsole(1);
QFile::remove("/etc/profile.d/ssh-luks-prompt.sh");
}
void BootMenuDialog::startSSHserverIfEnabled()
{
QByteArray sshkey = _i->bootParam("ssh_authorized_key").replace("\\n", "\n");
if (sshkey.isEmpty())
return;
/* Write authorized_key provided through boot parameter to tmpfs */
::chmod("/root", 0700);
::mkdir("/root/.ssh", 0700);
QFile f("/root/.ssh/authorized_keys");
f.open(f.WriteOnly);
f.write(sshkey);
f.close();
/* Let dropbear store the host's public key on the FAT partition */
mountSystemPartition();
QProgressDialog qpd(tr("Starting SSH server\nCan take a while on first run..."), QString(), 0, 0, this);
qpd.show();
QApplication::processEvents();
QFile::link("/boot", "/etc/dropbear");
::mkdir("/dev/pts", 0755);
QProcess::execute("mount -t devpts devpts /dev/pts");
QProcess::execute("/etc/init.d/S50dropbear start");
umountSystemPartition();
}
void BootMenuDialog::stopSSHserver()
{
if (!_i->bootParam("ssh_authorized_key").isEmpty())
{
QProcess::execute("/etc/init.d/S50dropbear stop");
QProcess::execute("umount /dev/pts");
}
}