mirror of
https://github.com/cmclark00/retro-imager.git
synced 2025-05-18 07:55:21 +01:00
Shift+Ctrl+X for advanced users that likes to customize the image
- Adds "hidden" shift+ctrl+X shortcut for eXpert image customization options. Allows one to set certain options on RPI OS images, namely: * disable overscan * set hostname * enable ssh and - set Pi user password if using password authentication OR - set authorized_keys (if running Imager on Linux/Mac this will have contents of ~/.ssh/id_rsa.pub prefilled) * configure wifi settings (if computer running Imager is connected by wifi it will prefill wifi SSID and if on Windows also PSK). * set time zone and keyboard layout Related to feature requests/issues: Ref #127 Ref #86 Ref #102 Ref #73 Ref #68 Ref #25 Ref #12 - Option Window also allows setting a couple other general settings: * Adds option for audible notification (beep) when imaging completes. Closes #46 * Adds option not to eject media when done. Closes #144 - No longer suspends a number of Windows services during Imaging (We want Windows to detect the drive and mount it, or we may not be able to alter files on FAT partition).
This commit is contained in:
parent
2844b5bd1a
commit
86f893388c
17 changed files with 2563 additions and 34 deletions
|
@ -40,6 +40,9 @@ DownloadThread::DownloadThread(const QByteArray &url, const QByteArray &localfil
|
|||
if (!_curlCount)
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
_curlCount++;
|
||||
|
||||
QSettings settings;
|
||||
_ejectEnabled = settings.value("eject", true).toBool();
|
||||
}
|
||||
|
||||
DownloadThread::~DownloadThread()
|
||||
|
@ -124,15 +127,15 @@ bool DownloadThread::_openAndPrepareDevice()
|
|||
|
||||
if (std::regex_match(_filename.constData(), m, windriveregex))
|
||||
{
|
||||
QByteArray nr = QByteArray::fromStdString(m[1]);
|
||||
QByteArray _nr = QByteArray::fromStdString(m[1]);
|
||||
|
||||
if (!nr.isEmpty()) {
|
||||
qDebug() << "Removing partition table from Windows drive #" << nr << "(" << _filename << ")";
|
||||
if (!_nr.isEmpty()) {
|
||||
qDebug() << "Removing partition table from Windows drive #" << _nr << "(" << _filename << ")";
|
||||
|
||||
QProcess proc;
|
||||
proc.start("diskpart");
|
||||
proc.waitForStarted();
|
||||
proc.write("select disk "+nr+"\r\n"
|
||||
proc.write("select disk "+_nr+"\r\n"
|
||||
"clean\r\n"
|
||||
"rescan\r\n");
|
||||
proc.closeWriteChannel();
|
||||
|
@ -665,14 +668,6 @@ void DownloadThread::_writeComplete()
|
|||
|
||||
emit finalizing();
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
// Temporarily stop storage services to prevent \[System Volume Information]\WPSettings.dat being created
|
||||
QProcess p1;
|
||||
QStringList args = {"stop", "StorSvc"};
|
||||
qDebug() << "Stopping storage services";
|
||||
p1.execute("net", args);
|
||||
#endif
|
||||
|
||||
if (_firstBlock)
|
||||
{
|
||||
qDebug() << "Writing first block (which we skipped at first)";
|
||||
|
@ -696,14 +691,18 @@ void DownloadThread::_writeComplete()
|
|||
QThread::sleep(1);
|
||||
_filename.replace("/dev/rdisk", "/dev/disk");
|
||||
#endif
|
||||
eject_disk(_filename.constData());
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QStringList args2 = {"start", "StorSvc"};
|
||||
QProcess *p2 = new QProcess(this);
|
||||
qDebug() << "Restarting storage services";
|
||||
p2->startDetached("net", args2);
|
||||
#endif
|
||||
if (_ejectEnabled && _config.isEmpty() && _cmdline.isEmpty())
|
||||
eject_disk(_filename.constData());
|
||||
|
||||
if (!_config.isEmpty() || !_cmdline.isEmpty())
|
||||
{
|
||||
if (!_customizeImage())
|
||||
return;
|
||||
|
||||
if (_ejectEnabled)
|
||||
eject_disk(_filename.constData());
|
||||
}
|
||||
|
||||
emit success();
|
||||
}
|
||||
|
@ -797,3 +796,201 @@ qint64 DownloadThread::_sectorsWritten()
|
|||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
void DownloadThread::setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun)
|
||||
{
|
||||
_config = config;
|
||||
_cmdline = cmdline;
|
||||
_firstrun = firstrun;
|
||||
}
|
||||
|
||||
bool DownloadThread::_customizeImage()
|
||||
{
|
||||
QString folder;
|
||||
QByteArray devlower = _filename.toLower();
|
||||
|
||||
emit preparationStatusUpdate(tr("Waiting for FAT partition to be mounted"));
|
||||
|
||||
/* See if OS auto-mounted the device */
|
||||
for (int tries = 0; tries < 3; tries++)
|
||||
{
|
||||
QThread::sleep(1);
|
||||
auto l = Drivelist::ListStorageDevices();
|
||||
for (auto i : l)
|
||||
{
|
||||
if (QByteArray::fromStdString(i.device).toLower() == devlower && i.mountpoints.size())
|
||||
{
|
||||
folder = QByteArray::fromStdString(i.mountpoints.front());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
bool manualmount = false;
|
||||
|
||||
if (folder.isEmpty())
|
||||
{
|
||||
/* Manually mount folder */
|
||||
manualmount = true;
|
||||
QByteArray fatpartition = _filename;
|
||||
if (isdigit(fatpartition.at(fatpartition.length()-1)))
|
||||
fatpartition += "p1";
|
||||
else
|
||||
fatpartition += "1";
|
||||
|
||||
if (::access(devlower.constData(), W_OK) != 0)
|
||||
{
|
||||
/* Not running as root, try to outsource mounting to udisks2 */
|
||||
#ifndef QT_NO_DBUS
|
||||
UDisks2Api udisks2;
|
||||
folder = udisks2.mountDevice(fatpartition);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Running as root, attempt running mount directly */
|
||||
QTemporaryDir td;
|
||||
QStringList args;
|
||||
folder = td.path();
|
||||
args << fatpartition << folder;
|
||||
|
||||
if (QProcess::execute("mount", args) != 0)
|
||||
{
|
||||
emit error(tr("Error mounting FAT32 partition"));
|
||||
return false;
|
||||
}
|
||||
td.setAutoRemove(false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (folder.isEmpty())
|
||||
{
|
||||
//
|
||||
qDebug() << "drive info. searching for:" << devlower;
|
||||
auto l = Drivelist::ListStorageDevices();
|
||||
for (auto i : l)
|
||||
{
|
||||
qDebug() << "drive" << QByteArray::fromStdString(i.device).toLower();
|
||||
for (auto mp : i.mountpoints) {
|
||||
qDebug() << "mountpoint:" << QByteArray::fromStdString(mp);
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
emit error(tr("Operating system did not mount FAT32 partition"));
|
||||
return false;
|
||||
}
|
||||
|
||||
emit preparationStatusUpdate(tr("Customizing image"));
|
||||
|
||||
if (!_firstrun.isEmpty())
|
||||
{
|
||||
QFile f(folder+"/firstrun.sh");
|
||||
if (f.open(f.WriteOnly))
|
||||
{
|
||||
f.write(_firstrun);
|
||||
f.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
emit error(tr("Error creating firstrun.sh on FAT partition"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_config.isEmpty())
|
||||
{
|
||||
auto configItems = _config.split('\n');
|
||||
configItems.removeAll("");
|
||||
QByteArray config;
|
||||
|
||||
QFile f(folder+"/config.txt");
|
||||
if (f.exists() && f.open(f.ReadOnly))
|
||||
{
|
||||
config = f.readAll();
|
||||
f.close();
|
||||
}
|
||||
|
||||
for (QByteArray item : configItems)
|
||||
{
|
||||
if (config.contains("#"+item)) {
|
||||
/* Uncomment existing line */
|
||||
config.replace("#"+item, item);
|
||||
} else if (config.contains("\n"+item)) {
|
||||
/* config.txt already contains the line */
|
||||
} else {
|
||||
/* Append new line to config.txt */
|
||||
if (config.right(1) != "\n")
|
||||
config += "\n"+item+"\n";
|
||||
else
|
||||
config += item+"\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (f.open(f.WriteOnly))
|
||||
{
|
||||
f.write(config);
|
||||
f.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
emit error(tr("Error writing to config.txt on FAT partition"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_cmdline.isEmpty())
|
||||
{
|
||||
QByteArray cmdline;
|
||||
|
||||
QFile f(folder+"/cmdline.txt");
|
||||
if (f.exists() && f.open(f.ReadOnly))
|
||||
{
|
||||
cmdline = f.readAll().trimmed();
|
||||
f.close();
|
||||
}
|
||||
|
||||
cmdline += _cmdline;
|
||||
if (f.open(f.WriteOnly))
|
||||
{
|
||||
f.write(cmdline);
|
||||
f.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
emit error(tr("Error writing to cmdline.txt on FAT partition"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
emit finalizing();
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
if (manualmount)
|
||||
{
|
||||
if (::access(devlower.constData(), W_OK) != 0)
|
||||
{
|
||||
#ifndef QT_NO_DBUS
|
||||
UDisks2Api udisks2;
|
||||
udisks2.unmountDrive(devlower);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
QStringList args;
|
||||
args << folder;
|
||||
QProcess::execute("umount", args);
|
||||
QDir d;
|
||||
d.rmdir(folder);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
::sync();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue