/* * Copyright (c) 2010, Haiku, Inc. * Distributed under the terms of the MIT license. * * Author: * Ɓukasz 'Sil2100' Zemczak */ #include "PackageInstall.h" #include "InstalledPackageInfo.h" #include "PackageItem.h" #include "PackageView.h" #include #include #include #include #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "PackageInstall" static int32 install_function(void* data) { // TODO: Inform if already one thread is running if (data == NULL) return -1; PackageInstall* install = static_cast(data); install->Install(); return 0; } PackageInstall::PackageInstall(PackageView* parent) : fParent(parent), fThreadId(-1), fCurrentScript(NULL) { } PackageInstall::~PackageInstall() { } status_t PackageInstall::Start() { status_t ret = B_OK; fIdLocker.Lock(); if (fThreadId > -1) { ret = B_BUSY; } else { fThreadId = spawn_thread(install_function, "install_package", B_NORMAL_PRIORITY, this); resume_thread(fThreadId); } fIdLocker.Unlock(); return ret; } void PackageInstall::Stop() { // TODO: Argh! No killing of threads!! That leaks resources which they // allocated. Rather inform them they need to quit, which they do at the // next convenient time, then use wait_for_thread() here. fIdLocker.Lock(); if (fThreadId > -1) { kill_thread(fThreadId); fThreadId = -1; } fIdLocker.Unlock(); fCurrentScriptLocker.Lock(); if (fCurrentScript != NULL) { thread_id id = fCurrentScript->GetThreadId(); if (id > -1) { fCurrentScript->SetThreadId(-1); kill_thread(id); } fCurrentScript = NULL; } fCurrentScriptLocker.Unlock(); } void PackageInstall::Install() { // A message sending wrapper around _Install() uint32 code = _Install(); BMessenger messenger(fParent); if (messenger.IsValid()) { BMessage message(code); messenger.SendMessage(&message); } } static inline BString get_item_progress_string(uint32 index, uint32 total) { BString label(B_TRANSLATE("%index% of %total%")); BString indexString; indexString << (index + 1); BString totalString; totalString << total; label.ReplaceAll("%index%", indexString); label.ReplaceAll("%total%", totalString); return label; } uint32 PackageInstall::_Install() { PackageInfo* info = fParent->GetPackageInfo(); pkg_profile* type = static_cast(info->GetProfile( fParent->CurrentType())); uint32 n = type->items.CountItems(); uint32 m = info->GetScriptCount(); PackageStatus* progress = fParent->StatusWindow(); progress->Reset(n + m + 5); progress->StageStep(1, B_TRANSLATE("Preparing package")); InstalledPackageInfo packageInfo(info->GetName(), info->GetVersion()); status_t err = packageInfo.InitCheck(); if (err == B_OK) { // The package is already installed, inform the user BAlert* reinstall = new BAlert("reinstall", B_TRANSLATE("The given package seems to be already installed on " "your system. Would you like to uninstall the existing one " "and continue the installation?"), B_TRANSLATE("Continue"), B_TRANSLATE("Abort")); reinstall->SetShortcut(1, B_ESCAPE); if (reinstall->Go() == 0) { // Uninstall the package err = packageInfo.Uninstall(); if (err != B_OK) { fprintf(stderr, "Error uninstalling previously installed " "package: %s\n", strerror(err)); // Ignore error } err = packageInfo.SetTo(info->GetName(), info->GetVersion(), true); if (err != B_OK) { fprintf(stderr, "Error marking installation of package: " "%s\n", strerror(err)); return P_MSG_I_ERROR; } } else { // Abort the installation return P_MSG_I_ABORT; } } else if (err == B_ENTRY_NOT_FOUND) { err = packageInfo.SetTo(info->GetName(), info->GetVersion(), true); if (err != B_OK) { fprintf(stderr, "Error marking installation of package: " "%s\n", strerror(err)); return P_MSG_I_ERROR; } } else if (progress->Stopped()) { return P_MSG_I_ABORT; } else { fprintf(stderr, "returning on error\n"); return P_MSG_I_ERROR; } progress->StageStep(1, B_TRANSLATE("Installing files and folders")); // Install files and directories packageInfo.SetName(info->GetName()); // TODO: Here's a small problem, since right now it's not quite sure // which description is really used as such. The one displayed on // the installer is mostly package installation description, but // most people use it for describing the application in more detail // then in the short description. // For now, we'll use the short description if possible. BString description = info->GetShortDescription(); if (description.Length() <= 0) description = info->GetDescription(); packageInfo.SetDescription(description.String()); packageInfo.SetSpaceNeeded(type->space_needed); fItemExistsPolicy = P_EXISTS_NONE; const char* installPath = fParent->CurrentPath()->Path(); for (uint32 i = 0; i < n; i++) { ItemState state(fItemExistsPolicy); PackageItem* item = static_cast(type->items.ItemAt(i)); err = item->DoInstall(installPath, &state); if (err == B_FILE_EXISTS) { // Writing to path failed because path already exists - ask the user // what to do and retry the writing process int32 choice = fParent->ItemExists(*item, state.destination, fItemExistsPolicy); if (choice != P_EXISTS_ABORT) { state.policy = choice; err = item->DoInstall(installPath, &state); } } if (err != B_OK) { fprintf(stderr, "Error '%s' while writing path\n", strerror(err)); return P_MSG_I_ERROR; } if (progress->Stopped()) return P_MSG_I_ABORT; // Update progress progress->StageStep(1, NULL, get_item_progress_string(i, n).String()); // Mark installed item in packageInfo packageInfo.AddItem(state.destination.Path()); } progress->StageStep(1, B_TRANSLATE("Running post-installation scripts"), ""); // Run all scripts // TODO: Change current working directory to installation location! for (uint32 i = 0; i < m; i++) { PackageScript* script = info->GetScript(i); fCurrentScriptLocker.Lock(); fCurrentScript = script; status_t status = script->DoInstall(installPath); if (status != B_OK) { fprintf(stderr, "Error while running script: %s\n", strerror(status)); fCurrentScriptLocker.Unlock(); return P_MSG_I_ERROR; } fCurrentScriptLocker.Unlock(); wait_for_thread(script->GetThreadId(), &status); fCurrentScriptLocker.Lock(); script->SetThreadId(-1); fCurrentScript = NULL; fCurrentScriptLocker.Unlock(); if (progress->Stopped()) return P_MSG_I_ABORT; progress->StageStep(1, NULL, get_item_progress_string(i, m).String()); } progress->StageStep(1, B_TRANSLATE("Finishing installation"), ""); err = packageInfo.Save(); if (err != B_OK) return P_MSG_I_ERROR; progress->StageStep(1, B_TRANSLATE("Done")); // Inform our parent that we finished return P_MSG_I_FINISHED; }