summaryrefslogtreecommitdiffstats
path: root/application/UpdateController.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'application/UpdateController.cpp')
-rw-r--r--application/UpdateController.cpp736
1 files changed, 368 insertions, 368 deletions
diff --git a/application/UpdateController.cpp b/application/UpdateController.cpp
index 8df5b3fc..0309ad93 100644
--- a/application/UpdateController.cpp
+++ b/application/UpdateController.cpp
@@ -27,423 +27,423 @@
#endif
static QFile::Permissions unixModeToPermissions(const int mode)
{
- QFile::Permissions perms;
+ QFile::Permissions perms;
- if (mode & S_IRUSR)
- {
- perms |= QFile::ReadUser;
- }
- if (mode & S_IWUSR)
- {
- perms |= QFile::WriteUser;
- }
- if (mode & S_IXUSR)
- {
- perms |= QFile::ExeUser;
- }
+ if (mode & S_IRUSR)
+ {
+ perms |= QFile::ReadUser;
+ }
+ if (mode & S_IWUSR)
+ {
+ perms |= QFile::WriteUser;
+ }
+ if (mode & S_IXUSR)
+ {
+ perms |= QFile::ExeUser;
+ }
- if (mode & S_IRGRP)
- {
- perms |= QFile::ReadGroup;
- }
- if (mode & S_IWGRP)
- {
- perms |= QFile::WriteGroup;
- }
- if (mode & S_IXGRP)
- {
- perms |= QFile::ExeGroup;
- }
+ if (mode & S_IRGRP)
+ {
+ perms |= QFile::ReadGroup;
+ }
+ if (mode & S_IWGRP)
+ {
+ perms |= QFile::WriteGroup;
+ }
+ if (mode & S_IXGRP)
+ {
+ perms |= QFile::ExeGroup;
+ }
- if (mode & S_IROTH)
- {
- perms |= QFile::ReadOther;
- }
- if (mode & S_IWOTH)
- {
- perms |= QFile::WriteOther;
- }
- if (mode & S_IXOTH)
- {
- perms |= QFile::ExeOther;
- }
- return perms;
+ if (mode & S_IROTH)
+ {
+ perms |= QFile::ReadOther;
+ }
+ if (mode & S_IWOTH)
+ {
+ perms |= QFile::WriteOther;
+ }
+ if (mode & S_IXOTH)
+ {
+ perms |= QFile::ExeOther;
+ }
+ return perms;
}
static const QLatin1String liveCheckFile("live.check");
UpdateController::UpdateController(QWidget * parent, const QString& root, const QString updateFilesDir, GoUpdate::OperationList operations)
{
- m_parent = parent;
- m_root = root;
- m_updateFilesDir = updateFilesDir;
- m_operations = operations;
+ m_parent = parent;
+ m_root = root;
+ m_updateFilesDir = updateFilesDir;
+ m_operations = operations;
}
void UpdateController::installUpdates()
{
- qint64 pid = -1;
- QStringList args;
- bool started = false;
+ qint64 pid = -1;
+ QStringList args;
+ bool started = false;
- qDebug() << "Installing updates.";
+ qDebug() << "Installing updates.";
#ifdef Q_OS_WIN
- QString finishCmd = QApplication::applicationFilePath();
+ QString finishCmd = QApplication::applicationFilePath();
#elif defined Q_OS_LINUX
- QString finishCmd = FS::PathCombine(m_root, "MultiMC");
+ QString finishCmd = FS::PathCombine(m_root, "MultiMC");
#elif defined Q_OS_MAC
- QString finishCmd = QApplication::applicationFilePath();
+ QString finishCmd = QApplication::applicationFilePath();
#else
#error Unsupported operating system.
#endif
- QString backupPath = FS::PathCombine(m_root, "update", "backup");
- QDir origin(m_root);
+ QString backupPath = FS::PathCombine(m_root, "update", "backup");
+ QDir origin(m_root);
- // clean up the backup folder. it should be empty before we start
- if(!FS::deletePath(backupPath))
- {
- qWarning() << "couldn't remove previous backup folder" << backupPath;
- }
- // and it should exist.
- if(!FS::ensureFolderPathExists(backupPath))
- {
- qWarning() << "couldn't create folder" << backupPath;
- return;
- }
+ // clean up the backup folder. it should be empty before we start
+ if(!FS::deletePath(backupPath))
+ {
+ qWarning() << "couldn't remove previous backup folder" << backupPath;
+ }
+ // and it should exist.
+ if(!FS::ensureFolderPathExists(backupPath))
+ {
+ qWarning() << "couldn't create folder" << backupPath;
+ return;
+ }
- bool useXPHack = false;
- QString exePath;
- QString exeOrigin;
- QString exeBackup;
+ bool useXPHack = false;
+ QString exePath;
+ QString exeOrigin;
+ QString exeBackup;
- // perform the update operations
- for(auto op: m_operations)
- {
- switch(op.type)
- {
- // replace = move original out to backup, if it exists, move the new file in its place
- case GoUpdate::Operation::OP_REPLACE:
- {
+ // perform the update operations
+ for(auto op: m_operations)
+ {
+ switch(op.type)
+ {
+ // replace = move original out to backup, if it exists, move the new file in its place
+ case GoUpdate::Operation::OP_REPLACE:
+ {
#ifdef Q_OS_WIN32
- // hack for people renaming the .exe because ... reasons :)
- if(op.destination == "MultiMC.exe")
- {
- op.destination = QFileInfo(QApplication::applicationFilePath()).fileName();
- }
+ // hack for people renaming the .exe because ... reasons :)
+ if(op.destination == "MultiMC.exe")
+ {
+ op.destination = QFileInfo(QApplication::applicationFilePath()).fileName();
+ }
#endif
- QFileInfo destination (FS::PathCombine(m_root, op.destination));
+ QFileInfo destination (FS::PathCombine(m_root, op.destination));
#ifdef Q_OS_WIN32
- if(QSysInfo::windowsVersion() < QSysInfo::WV_VISTA)
- {
- if(destination.fileName() == "MultiMC.exe")
- {
- QDir rootDir(m_root);
- exeOrigin = rootDir.relativeFilePath(op.source);
- exePath = rootDir.relativeFilePath(op.destination);
- exeBackup = rootDir.relativeFilePath(FS::PathCombine(backupPath, destination.fileName()));
- useXPHack = true;
- continue;
- }
- }
+ if(QSysInfo::windowsVersion() < QSysInfo::WV_VISTA)
+ {
+ if(destination.fileName() == "MultiMC.exe")
+ {
+ QDir rootDir(m_root);
+ exeOrigin = rootDir.relativeFilePath(op.source);
+ exePath = rootDir.relativeFilePath(op.destination);
+ exeBackup = rootDir.relativeFilePath(FS::PathCombine(backupPath, destination.fileName()));
+ useXPHack = true;
+ continue;
+ }
+ }
#endif
- if(destination.exists())
- {
- QString backupName = op.destination;
- backupName.replace('/', '_');
- QString backupFilePath = FS::PathCombine(backupPath, backupName);
- if(!QFile::rename(destination.absoluteFilePath(), backupFilePath))
- {
- qWarning() << "Couldn't move:" << destination.absoluteFilePath() << "to" << backupFilePath;
- m_failedOperationType = Replace;
- m_failedFile = op.destination;
- fail();
- return;
- }
- BackupEntry be;
- be.original = destination.absoluteFilePath();
- be.backup = backupFilePath;
- be.update = op.source;
- m_replace_backups.append(be);
- }
- // make sure the folder we are putting this into exists
- if(!FS::ensureFilePathExists(destination.absoluteFilePath()))
- {
- qWarning() << "REPLACE: Couldn't create folder:" << destination.absoluteFilePath();
- m_failedOperationType = Replace;
- m_failedFile = op.destination;
- fail();
- return;
- }
- // now move the new file in
- if(!QFile::rename(op.source, destination.absoluteFilePath()))
- {
- qWarning() << "REPLACE: Couldn't move:" << op.source << "to" << destination.absoluteFilePath();
- m_failedOperationType = Replace;
- m_failedFile = op.destination;
- fail();
- return;
- }
- QFile::setPermissions(destination.absoluteFilePath(), unixModeToPermissions(op.destinationMode));
- }
- break;
- // delete = move original to backup
- case GoUpdate::Operation::OP_DELETE:
- {
- QString destFilePath = FS::PathCombine(m_root, op.destination);
- if(QFile::exists(destFilePath))
- {
- QString backupName = op.destination;
- backupName.replace('/', '_');
- QString trashFilePath = FS::PathCombine(backupPath, backupName);
+ if(destination.exists())
+ {
+ QString backupName = op.destination;
+ backupName.replace('/', '_');
+ QString backupFilePath = FS::PathCombine(backupPath, backupName);
+ if(!QFile::rename(destination.absoluteFilePath(), backupFilePath))
+ {
+ qWarning() << "Couldn't move:" << destination.absoluteFilePath() << "to" << backupFilePath;
+ m_failedOperationType = Replace;
+ m_failedFile = op.destination;
+ fail();
+ return;
+ }
+ BackupEntry be;
+ be.original = destination.absoluteFilePath();
+ be.backup = backupFilePath;
+ be.update = op.source;
+ m_replace_backups.append(be);
+ }
+ // make sure the folder we are putting this into exists
+ if(!FS::ensureFilePathExists(destination.absoluteFilePath()))
+ {
+ qWarning() << "REPLACE: Couldn't create folder:" << destination.absoluteFilePath();
+ m_failedOperationType = Replace;
+ m_failedFile = op.destination;
+ fail();
+ return;
+ }
+ // now move the new file in
+ if(!QFile::rename(op.source, destination.absoluteFilePath()))
+ {
+ qWarning() << "REPLACE: Couldn't move:" << op.source << "to" << destination.absoluteFilePath();
+ m_failedOperationType = Replace;
+ m_failedFile = op.destination;
+ fail();
+ return;
+ }
+ QFile::setPermissions(destination.absoluteFilePath(), unixModeToPermissions(op.destinationMode));
+ }
+ break;
+ // delete = move original to backup
+ case GoUpdate::Operation::OP_DELETE:
+ {
+ QString destFilePath = FS::PathCombine(m_root, op.destination);
+ if(QFile::exists(destFilePath))
+ {
+ QString backupName = op.destination;
+ backupName.replace('/', '_');
+ QString trashFilePath = FS::PathCombine(backupPath, backupName);
- if(!QFile::rename(destFilePath, trashFilePath))
- {
- qWarning() << "DELETE: Couldn't move:" << op.destination << "to" << trashFilePath;
- m_failedFile = op.destination;
- m_failedOperationType = Delete;
- fail();
- return;
- }
- BackupEntry be;
- be.original = destFilePath;
- be.backup = trashFilePath;
- m_delete_backups.append(be);
- }
- }
- break;
- }
- }
+ if(!QFile::rename(destFilePath, trashFilePath))
+ {
+ qWarning() << "DELETE: Couldn't move:" << op.destination << "to" << trashFilePath;
+ m_failedFile = op.destination;
+ m_failedOperationType = Delete;
+ fail();
+ return;
+ }
+ BackupEntry be;
+ be.original = destFilePath;
+ be.backup = trashFilePath;
+ m_delete_backups.append(be);
+ }
+ }
+ break;
+ }
+ }
- // try to start the new binary
- args = qApp->arguments();
- args.removeFirst();
+ // try to start the new binary
+ args = qApp->arguments();
+ args.removeFirst();
- // on old Windows, do insane things... no error checking here, this is just to have something.
- if(useXPHack)
- {
- QString script;
- auto nativePath = QDir::toNativeSeparators(exePath);
- auto nativeOriginPath = QDir::toNativeSeparators(exeOrigin);
- auto nativeBackupPath = QDir::toNativeSeparators(exeBackup);
+ // on old Windows, do insane things... no error checking here, this is just to have something.
+ if(useXPHack)
+ {
+ QString script;
+ auto nativePath = QDir::toNativeSeparators(exePath);
+ auto nativeOriginPath = QDir::toNativeSeparators(exeOrigin);
+ auto nativeBackupPath = QDir::toNativeSeparators(exeBackup);
- // so we write this vbscript thing...
- QTextStream out(&script);
- out << "WScript.Sleep 1000\n";
- out << "Set fso=CreateObject(\"Scripting.FileSystemObject\")\n";
- out << "Set shell=CreateObject(\"WScript.Shell\")\n";
- out << "fso.MoveFile \"" << nativePath << "\", \"" << nativeBackupPath << "\"\n";
- out << "fso.MoveFile \"" << nativeOriginPath << "\", \"" << nativePath << "\"\n";
- out << "shell.Run \"" << nativePath << "\"\n";
+ // so we write this vbscript thing...
+ QTextStream out(&script);
+ out << "WScript.Sleep 1000\n";
+ out << "Set fso=CreateObject(\"Scripting.FileSystemObject\")\n";
+ out << "Set shell=CreateObject(\"WScript.Shell\")\n";
+ out << "fso.MoveFile \"" << nativePath << "\", \"" << nativeBackupPath << "\"\n";
+ out << "fso.MoveFile \"" << nativeOriginPath << "\", \"" << nativePath << "\"\n";
+ out << "shell.Run \"" << nativePath << "\"\n";
- QString scriptPath = FS::PathCombine(m_root, "update", "update.vbs");
+ QString scriptPath = FS::PathCombine(m_root, "update", "update.vbs");
- // we save it
- QFile scriptFile(scriptPath);
- scriptFile.open(QIODevice::WriteOnly);
- scriptFile.write(script.toLocal8Bit().replace("\n", "\r\n"));
- scriptFile.close();
+ // we save it
+ QFile scriptFile(scriptPath);
+ scriptFile.open(QIODevice::WriteOnly);
+ scriptFile.write(script.toLocal8Bit().replace("\n", "\r\n"));
+ scriptFile.close();
- // we run it
- started = QProcess::startDetached("wscript", {scriptPath}, m_root);
+ // we run it
+ started = QProcess::startDetached("wscript", {scriptPath}, m_root);
- // and we quit. conscious thought.
- qApp->quit();
- return;
- }
- bool doLiveCheck = true;
- bool startFailed = false;
+ // and we quit. conscious thought.
+ qApp->quit();
+ return;
+ }
+ bool doLiveCheck = true;
+ bool startFailed = false;
- // remove live check file, if any
- if(QFile::exists(liveCheckFile))
- {
- if(!QFile::remove(liveCheckFile))
- {
- qWarning() << "Couldn't remove the" << liveCheckFile << "file! We will proceed without :(";
- doLiveCheck = false;
- }
- }
+ // remove live check file, if any
+ if(QFile::exists(liveCheckFile))
+ {
+ if(!QFile::remove(liveCheckFile))
+ {
+ qWarning() << "Couldn't remove the" << liveCheckFile << "file! We will proceed without :(";
+ doLiveCheck = false;
+ }
+ }
- if(doLiveCheck)
- {
- if(!args.contains("--alive"))
- {
- args.append("--alive");
- }
- }
+ if(doLiveCheck)
+ {
+ if(!args.contains("--alive"))
+ {
+ args.append("--alive");
+ }
+ }
- // FIXME: reparse args and construct a safe variant from scratch. This is a workaround for GH-1874:
- QStringList realargs;
- int skip = 0;
- for(auto & arg: args)
- {
- if(skip)
- {
- skip--;
- continue;
- }
- if(arg == "-l")
- {
- skip = 1;
- continue;
- }
- realargs.append(arg);
- }
+ // FIXME: reparse args and construct a safe variant from scratch. This is a workaround for GH-1874:
+ QStringList realargs;
+ int skip = 0;
+ for(auto & arg: args)
+ {
+ if(skip)
+ {
+ skip--;
+ continue;
+ }
+ if(arg == "-l")
+ {
+ skip = 1;
+ continue;
+ }
+ realargs.append(arg);
+ }
- // start the updated application
- started = QProcess::startDetached(finishCmd, realargs, QDir::currentPath(), &pid);
- // much dumber check - just find out if the call
- if(!started || pid == -1)
- {
- qWarning() << "Couldn't start new process properly!";
- startFailed = true;
- }
- if(!startFailed && doLiveCheck)
- {
- int attempts = 0;
- while(attempts < 10)
- {
- attempts++;
- QString key;
- std::this_thread::sleep_for(std::chrono::milliseconds(250));
- if(!QFile::exists(liveCheckFile))
- {
- qWarning() << "Couldn't find the" << liveCheckFile << "file!";
- startFailed = true;
- continue;
- }
- try
- {
- key = QString::fromUtf8(FS::read(liveCheckFile));
- auto id = ApplicationId::fromRawString(key);
- LocalPeer peer(nullptr, id);
- if(peer.isClient())
- {
- startFailed = false;
- qDebug() << "Found process started with key " << key;
- break;
- }
- else
- {
- startFailed = true;
- qDebug() << "Process started with key " << key << "apparently died or is not reponding...";
- break;
- }
- }
- catch(Exception e)
- {
- qWarning() << "Couldn't read the" << liveCheckFile << "file!";
- startFailed = true;
- continue;
- }
- }
- }
- if(startFailed)
- {
- m_failedOperationType = Start;
- fail();
- return;
- }
- else
- {
- origin.rmdir(m_updateFilesDir);
- qApp->quit();
- return;
- }
+ // start the updated application
+ started = QProcess::startDetached(finishCmd, realargs, QDir::currentPath(), &pid);
+ // much dumber check - just find out if the call
+ if(!started || pid == -1)
+ {
+ qWarning() << "Couldn't start new process properly!";
+ startFailed = true;
+ }
+ if(!startFailed && doLiveCheck)
+ {
+ int attempts = 0;
+ while(attempts < 10)
+ {
+ attempts++;
+ QString key;
+ std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ if(!QFile::exists(liveCheckFile))
+ {
+ qWarning() << "Couldn't find the" << liveCheckFile << "file!";
+ startFailed = true;
+ continue;
+ }
+ try
+ {
+ key = QString::fromUtf8(FS::read(liveCheckFile));
+ auto id = ApplicationId::fromRawString(key);
+ LocalPeer peer(nullptr, id);
+ if(peer.isClient())
+ {
+ startFailed = false;
+ qDebug() << "Found process started with key " << key;
+ break;
+ }
+ else
+ {
+ startFailed = true;
+ qDebug() << "Process started with key " << key << "apparently died or is not reponding...";
+ break;
+ }
+ }
+ catch (const Exception &e)
+ {
+ qWarning() << "Couldn't read the" << liveCheckFile << "file!";
+ startFailed = true;
+ continue;
+ }
+ }
+ }
+ if(startFailed)
+ {
+ m_failedOperationType = Start;
+ fail();
+ return;
+ }
+ else
+ {
+ origin.rmdir(m_updateFilesDir);
+ qApp->quit();
+ return;
+ }
}
void UpdateController::fail()
{
- qWarning() << "Update failed!";
+ qWarning() << "Update failed!";
- QString msg;
- bool doRollback = false;
- QString failTitle = QObject::tr("Update failed!");
- QString rollFailTitle = QObject::tr("Rollback failed!");
- switch (m_failedOperationType)
- {
- case Replace:
- {
- msg = QObject::tr("Couldn't replace file %1. Changes will be reverted.\n"
- "See the MultiMC log file for details.").arg(m_failedFile);
- doRollback = true;
- QMessageBox::critical(m_parent, failTitle, msg);
- break;
- }
- case Delete:
- {
- msg = QObject::tr("Couldn't remove file %1. Changes will be reverted.\n"
- "See the MultiMC log file for details.").arg(m_failedFile);
- doRollback = true;
- QMessageBox::critical(m_parent, failTitle, msg);
- break;
- }
- case Start:
- {
- msg = QObject::tr("The new version didn't start or is too old and doesn't respond to startup checks.\n"
- "\n"
- "Roll back to previous version?");
- auto result = QMessageBox::critical(
- m_parent,
- failTitle,
- msg,
- QMessageBox::Yes | QMessageBox::No,
- QMessageBox::Yes
- );
- doRollback = (result == QMessageBox::Yes);
- break;
- }
- case Nothing:
- default:
- return;
- }
- if(doRollback)
- {
- auto rollbackOK = rollback();
- if(!rollbackOK)
- {
- msg = QObject::tr("The rollback failed too.\n"
- "You will have to repair MultiMC manually.\n"
- "Please let us know why and how this happened.").arg(m_failedFile);
- QMessageBox::critical(m_parent, rollFailTitle, msg);
- qApp->quit();
- }
- }
- else
- {
- qApp->quit();
- }
+ QString msg;
+ bool doRollback = false;
+ QString failTitle = QObject::tr("Update failed!");
+ QString rollFailTitle = QObject::tr("Rollback failed!");
+ switch (m_failedOperationType)
+ {
+ case Replace:
+ {
+ msg = QObject::tr("Couldn't replace file %1. Changes will be reverted.\n"
+ "See the MultiMC log file for details.").arg(m_failedFile);
+ doRollback = true;
+ QMessageBox::critical(m_parent, failTitle, msg);
+ break;
+ }
+ case Delete:
+ {
+ msg = QObject::tr("Couldn't remove file %1. Changes will be reverted.\n"
+ "See the MultiMC log file for details.").arg(m_failedFile);
+ doRollback = true;
+ QMessageBox::critical(m_parent, failTitle, msg);
+ break;
+ }
+ case Start:
+ {
+ msg = QObject::tr("The new version didn't start or is too old and doesn't respond to startup checks.\n"
+ "\n"
+ "Roll back to previous version?");
+ auto result = QMessageBox::critical(
+ m_parent,
+ failTitle,
+ msg,
+ QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::Yes
+ );
+ doRollback = (result == QMessageBox::Yes);
+ break;
+ }
+ case Nothing:
+ default:
+ return;
+ }
+ if(doRollback)
+ {
+ auto rollbackOK = rollback();
+ if(!rollbackOK)
+ {
+ msg = QObject::tr("The rollback failed too.\n"
+ "You will have to repair MultiMC manually.\n"
+ "Please let us know why and how this happened.").arg(m_failedFile);
+ QMessageBox::critical(m_parent, rollFailTitle, msg);
+ qApp->quit();
+ }
+ }
+ else
+ {
+ qApp->quit();
+ }
}
bool UpdateController::rollback()
{
- bool revertOK = true;
- // if the above failed, roll back changes
- for(auto backup:m_replace_backups)
- {
- qWarning() << "restoring" << backup.original << "from" << backup.backup;
- if(!QFile::rename(backup.original, backup.update))
- {
- revertOK = false;
- qWarning() << "moving new" << backup.original << "back to" << backup.update << "failed!";
- continue;
- }
+ bool revertOK = true;
+ // if the above failed, roll back changes
+ for(auto backup:m_replace_backups)
+ {
+ qWarning() << "restoring" << backup.original << "from" << backup.backup;
+ if(!QFile::rename(backup.original, backup.update))
+ {
+ revertOK = false;
+ qWarning() << "moving new" << backup.original << "back to" << backup.update << "failed!";
+ continue;
+ }
- if(!QFile::rename(backup.backup, backup.original))
- {
- revertOK = false;
- qWarning() << "restoring" << backup.original << "failed!";
- }
- }
- for(auto backup:m_delete_backups)
- {
- qWarning() << "restoring" << backup.original << "from" << backup.backup;
- if(!QFile::rename(backup.backup, backup.original))
- {
- revertOK = false;
- qWarning() << "restoring" << backup.original << "failed!";
- }
- }
- return revertOK;
+ if(!QFile::rename(backup.backup, backup.original))
+ {
+ revertOK = false;
+ qWarning() << "restoring" << backup.original << "failed!";
+ }
+ }
+ for(auto backup:m_delete_backups)
+ {
+ qWarning() << "restoring" << backup.original << "from" << backup.backup;
+ if(!QFile::rename(backup.backup, backup.original))
+ {
+ revertOK = false;
+ qWarning() << "restoring" << backup.original << "failed!";
+ }
+ }
+ return revertOK;
}