This repository has been archived on 2022-08-16. You can view files and clone it, but cannot push or open issues or pull requests.
modern-linbo-gui/sources/backend/linbobackend.cpp

477 lines
16 KiB
C++

/****************************************************************************
** Modern Linbo GUI
** Copyright (C) 2020 Dorian Zedler <dorian@itsblue.de>
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as published
** by the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/
#include "linbobackend.h"
#define LINBO_CMD(arg) QStringList("linbo_cmd") << (arg)
using namespace std;
LinboBackend::LinboBackend(QObject *parent) : QObject(parent)
{
this->setState(Initializing);
this->logger = new LinboLogger(this);
this->config = new LinboConfig(this);
this->autostartTimer = new QTimer(this);
this->autostartTimer->setSingleShot(true);
this->autostartRemainingTimeRefreshTimer = new QTimer(this);
this->autostartRemainingTimeRefreshTimer->setSingleShot(true);
this->autostartRemainingTimeRefreshTimer->setInterval(10);
this->autostartElapsedTimer = new QElapsedTimer();
this->currentOs = nullptr;
// read start.conf
this->logger->log("Starting to parse start.conf", LinboLogger::LinboGuiInfo);
ifstream input;
input.open( "start.conf", ios_base::in );
QString tmp_qstring;
while( !input.eof() ) {
// *** Image description section ***
// entry in start tab
char line[500];
input.getline(line,500,'\n');
tmp_qstring = QString::fromAscii( line, -1 ).stripWhiteSpace();
if ( tmp_qstring.startsWith("#") || tmp_qstring.isEmpty() ) continue;
tmp_qstring = tmp_qstring.section("#",0,0).stripWhiteSpace(); // Strip comment
if(tmp_qstring.lower().compare("[os]") == 0) {
LinboOs* tmpOs = read_os(&input);
if(!tmpOs->getName().isEmpty()) {
this->operatingSystems.append(tmpOs);
if(tmpOs->getAutostart() && this->currentOs == nullptr)
this->currentOs = tmpOs;
// check if this is an additional/incremental image for an existing OS
/* TODO unsigned int i; // Being checked later.
for(i = 0; i < elements.size(); i++ ) {
if(tmp_os.get_name().lower().compare(elements[i].get_name().lower()) == 0) {
elements[i].image_history.push_back(tmp_image); break;
}
}
if(i==elements.size()) { // Not included yet -> new image
tmp_os.image_history.push_back(tmp_image);
elements.push_back(tmp_os);
}*/
}
else {
tmpOs->deleteLater();
}
} else if(tmp_qstring.lower().compare("[linbo]") == 0) {
read_globals(&input, config);
} else if(tmp_qstring.lower().compare("[partition]") == 0) {
LinboDiskPartition* tmpPartition = read_partition(&input);
if(!tmpPartition->getPath().isEmpty()) {
diskPartitions.append(tmpPartition);
}
else {
tmpPartition->deleteLater();
}
}
}
input.close();
this->logger->log("Finished to parsing start.conf", LinboLogger::LinboGuiInfo);
this->logger->log("Loading global configuration", LinboLogger::LinboGuiInfo);
// load global config
QStringList command;
// ascynchorons commands are logged to logger
this->asynchronosProcess = new QProcess();
connect( asynchronosProcess, SIGNAL(readyReadStandardOutput()),
this, SLOT(readFromStdout()) );
connect( asynchronosProcess, SIGNAL(readyReadStandardError()),
this, SLOT(readFromStderr()) );
// synchronos commands are not logged
this->synchronosProcess = new QProcess();
// client ip
this->config->setIpAddress(this->executeCommand(true, "ip"));
// mac address
this->config->setMacAddress(this->executeCommand(true, "mac"));
// Version
this->config->setVersion(this->executeCommand(true, "version").stripWhiteSpace());
// hostname
this->config->setHostname(this->executeCommand(true, "hostname"));
// CPU
this->config->setCpu(this->executeCommand(true, "cpu"));
// Memory
this->config->setRamSize(this->executeCommand(true, "memory"));
// Cache Size
this->config->setCacheSize(this->executeCommand(true, "size"));
// Harddisk Size
QRegExp *removePartition = new QRegExp("[0-9]{1,2}") ;
QString hd = this->config->getCache();
// e.g. turn /dev/sda1 into /dev/sda
hd.remove( *removePartition );
this->config->setHddSize(this->executeCommand(true, "size", hd));
this->logger->log("Finished loading global configuration", LinboLogger::LinboGuiInfo);
// default select first OS if no other OS has been selected yet
if(this->operatingSystems.length() > 0 && this->currentOs == nullptr)
this->currentOs = this->operatingSystems[0];
// triger autostart
if(this->currentOs->getAutostart())
this->executeAutostart();
else
this->setState(Idle);
}
// --------------------
// - Public functions -
// --------------------
void LinboBackend::executeAutostart() {
this->autostartTimer->setInterval(this->currentOs->getAutostartTimeout() * 1000);
this->autostartElapsedTimer->restart();
}
void handleAutostartTimerTimeout() {
}
void LinboBackend::shutdown() {
this->executeCommand(false, "busybox", QStringList("poweroff"));
}
void LinboBackend::reboot() {
this->executeCommand(false, "busybox", QStringList("reboot"));
}
bool LinboBackend::startCurrentOs() {
LinboOs* os = this->currentOs;
if(os == nullptr || this->state != Idle || !this->currentOs->getStartbutton())
return false;
this->setState(Starting);
return true;
this->executeCommand(
false,
"start",
os->getBootPartition(),
os->getRootPartition(),
os->getKernel(),
os->getInitrd(),
os->getKernelOptions(),
this->config->getCache()
);
return true;
}
bool LinboBackend::syncCurrentOs() {
LinboOs* os = this->currentOs;
if(os == nullptr || this->state != Idle || !this->currentOs->getSyncbutton())
return false;
this->setState(Syncing);
this->executeCommand(
false,
"syncstart",
this->config->getServer(),
this->config->getCache(),
os->getBaseImage()->getName(),
os->getDifferentialImage()->getName(),
os->getBootPartition(),
os->getRootPartition(),
os->getKernel(),
os->getInitrd(),
os->getKernelOptions()
);
return true;
}
bool LinboBackend::reinstallCurrentOs() {
LinboOs* os = this->currentOs;
if(os == nullptr || this->state != Idle || !this->currentOs->getNewbutton())
return false;
this->setState(Reinstalling);
this->executeCommand(
false,
"syncr",
this->config->getServer(),
this->config->getCache(),
os->getBaseImage()->getName(),
os->getDifferentialImage()->getName(),
os->getBootPartition(),
os->getRootPartition(),
os->getKernel(),
os->getInitrd(),
os->getKernelOptions(),
QString("force")
);
return true;
}
bool LinboBackend::partitionDrive(bool format) {
if(this->state != Root)
return false;
this->setState(Partitioning);
QStringList commandArgs = QStringList(format ? "partition":"partition_noformat");
for( int i=0; i < this->diskPartitions.length(); i++) {
LinboDiskPartition* p = this->diskPartitions[i];
commandArgs.append(
this->buildCommand(
p->getPath(),
QString::number(p->getSize()),
p->getId(),
QString((p->getBootable())?"bootable":"\" \""),
p->getFstype()
)
);
}
this->executeCommand(false, this->linboCmdCommand, commandArgs);
return true;
}
bool LinboBackend::initializeCache() {
if(this->state != Root)
return false;
this->setState(InitializingCache);
QStringList commandArgs = this->buildCommand("initcache", config->getServer(), config->getCache());
if( this->config->getDownloadType().isEmpty() )
commandArgs.append(this->config->getDownloadType());
else
commandArgs.append("rsync");
for(int i = 0; i < this->operatingSystems.length(); i++) {
LinboOs* os = this->operatingSystems[i];
commandArgs.append(this->buildCommand(os->getBaseImage()->getName(), os->getDifferentialImage()->getName()));
/* TODO ?? for(unsigned int j = 0; j < os[i].image_history.size(); j++) {
saveappend( command, os[i].image_history[j].get_image() );
}*/
}
this->executeCommand(false, this->linboCmdCommand, commandArgs);
return true;
}
bool LinboBackend::updateLinbo() {
if(this->state != Root)
return false;
this->executeCommand("update", this->config->getServer(), this->config->getCache());
return true;
}
LinboLogger* LinboBackend::getLogger() {
return this->logger;
}
LinboConfig* LinboBackend::getConfig() {
return this->config;
}
QList<LinboOs*> LinboBackend::getOperatingSystems() {
return this->operatingSystems;
}
LinboOs* LinboBackend::getCurrentOs() {
return this->currentOs;
}
void LinboBackend::setCurrentOs(LinboOs* os) {
if(this->state != Idle || !this->operatingSystems.contains(os) || this->currentOs == os)
return;
this->currentOs = os;
emit this->currentOsChanged(os);
}
double LinboBackend::getAutostartTimeoutProgress() {
return this->autostartElapsedTimer->elapsed() / this->autostartTimer->interval();
}
int LinboBackend::getAutostartTimeoutRemainingSeconds() {
return (this->autostartTimer->interval() - this->autostartElapsedTimer->elapsed()) / 1000;
}
// -----------
// - Helpers -
// -----------
QString LinboBackend::executeCommand(bool waitForFinished, QString command, QStringList commandArgs) {
this->logger->log("Executing " + QString(waitForFinished ? "synchronos":"asynchronos") + ": " + command + " " + commandArgs.join(" "), LinboLogger::LinboGuiInfo);
if(waitForFinished) {
// clear old output
this->synchronosProcess->readAll();
synchronosProcess->start(command, commandArgs);
synchronosProcess->waitForStarted();
while( !synchronosProcess->waitForFinished(10000) ) {}
return this->synchronosProcess->readAllStandardOutput();
}
else {
asynchronosProcess->start(command, commandArgs);
asynchronosProcess->waitForStarted();
return "";
}
}
void LinboBackend::readFromStdout() {
QString stdOut = this->asynchronosProcess->readAllStandardOutput();
QStringList lines = stdOut.split("\n");
for(QString line : lines) {
this->logger->log(line, LinboLogger::StdOut);
}
}
void LinboBackend::readFromStderr() {
QString stdOut = this->asynchronosProcess->readAllStandardError();
QStringList lines = stdOut.split("\n");
for(QString line : lines) {
this->logger->log(line, LinboLogger::StdErr);
}
}
LinboBackend::LinboState LinboBackend::getState() {
return this->state;
}
void LinboBackend::setState(LinboState state) {
if(this->state == state)
return;
this->state = state;
emit this->stateChanged(this->state);
}
// Return true unless beginning of new section '[' is found.
bool LinboBackend::read_pair(ifstream* input, QString& key, QString& value) {
char line[1024];
if(input->peek() == '[') return false; // Next section found.
input->getline(line,1024,'\n');
QString s = QString::fromAscii( line, -1 ).stripWhiteSpace();
key = s.section("=",0,0).stripWhiteSpace().lower();
if(s.startsWith("#")||key.isEmpty()) {
key = QString(""); value = QString("");
} else {
value=s.section("=",1).section("#",0,0).stripWhiteSpace();
}
return true;
}
bool LinboBackend::toBool(const QString& value) {
if(value.startsWith("yes",false)) return true;
if(value.startsWith("true",false)) return true;
if(value.startsWith("enable",false)) return true;
return false;
}
LinboOs* LinboBackend::read_os(ifstream* input) {
LinboOs* os = new LinboOs(this);
QString key, value;
while(!input->eof() && read_pair(input, key, value)) {
if(key.compare("name") == 0) os->setName(value);
else if(key.compare("description") == 0) os->setDescription(value);
else if(key.compare("version") == 0) os->setVersion(value);
else if(key.compare("iconname") == 0) os->setIconName(value);
else if(key.compare("image") == 0) os->setDifferentialImage(new LinboImage(value, os));
else if(key.compare("baseimage") == 0) os->setBaseImage(new LinboImage(value, os));
else if(key.compare("boot") == 0) os->setBootPartition(value);
else if(key.compare("root") == 0) os->setRootPartition(value);
else if(key.compare("kernel") == 0) os->setKernel(value);
else if(key.compare("initrd") == 0) os->setInitrd(value);
else if(key.compare("append") == 0) os->setKernelOptions(value);
else if(key.compare("syncenabled") == 0) os->setSyncButton(toBool(value));
else if(key.compare("startenabled") == 0) os->setStartButton(toBool(value));
else if((key.compare("remotesyncenabled") == 0) || (key.compare("newenabled") == 0)) os->setNewButton(toBool(value));
else if(key.compare("defaultaction") == 0) os->setDefaultAction(os->startActionFromString(value));
else if(key.compare("autostart") == 0) os->setAutostart(toBool(value));
else if(key.compare("autostarttimeout") == 0) os->setAutostartTimeout(value.toInt());
else if(key.compare("hidden") == 0) os->setHidden(toBool(value));
}
return os;
}
LinboDiskPartition* LinboBackend::read_partition(ifstream* input) {
LinboDiskPartition* partition = new LinboDiskPartition(this);
QString key, value;
while(!input->eof() && read_pair(input, key, value)) {
if(key.compare("dev") == 0) partition->setPath(value);
else if(key.compare("size") == 0) partition->setSize(value.toInt());
else if(key.compare("id") == 0) partition->setId(value);
else if(key.compare("fstype") == 0) partition->setFstype(value);
else if(key.startsWith("bootable", false)) partition->setBootable(toBool(value));
}
return partition;
}
void LinboBackend::read_globals( ifstream* input, LinboConfig* config ) {
QString key, value;
while(!input->eof() && read_pair(input, key, value)) {
if(key.compare("server") == 0) config->setServer(value);
else if(key.compare("cache") == 0) config->setCache(value);
else if(key.compare("roottimeout") == 0) config->setRootTimeout((unsigned int)value.toInt());
else if(key.compare("group") == 0) config->setHostgroup(value);
else if(key.compare("autopartition") == 0) config->setAutopartition(toBool(value));
else if(key.compare("autoinitcache") == 0) config->setAutoInitCache(toBool(value));
else if(key.compare("autoformat") == 0) config->setAutoFormat(toBool(value));
else if(key.compare("backgroundfontcolor") == 0) config->setBackgroundFontcolor(value);
else if(key.compare("consolefontcolorstdout") == 0) config->setConsoleFontcolorStdout(value);
else if(key.compare("consolefontcolorstderr") == 0) config->setConsoleFontcolorStderr(value);
else if(key.compare("usemulticast") == 0) {
if( (unsigned int)value.toInt() == 0 )
config->setDownloadType("rsync");
else
config->setDownloadType("multicast");
}
else if(key.compare("downloadtype") == 0) config->setDownloadType(value);
}
}