1/* 2 * Copyright 2013-2021, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ingo Weinhold <ingo_weinhold@gmx.de> 7 * Stephan A��mus <superstippi@gmx.de> 8 * Rene Gollent <rene@gollent.com> 9 * Julian Harnath <julian.harnath@rwth-aachen.de> 10 * Andrew Lindesay <apl@lindesay.co.nz> 11 * 12 * Note that this file has been re-factored from `PackageManager.cpp` and 13 * authors have been carried across in 2021. 14 */ 15 16 17#include "InstallPackageProcess.h" 18 19#include <algorithm> 20 21#include <AutoLocker.h> 22#include <Catalog.h> 23#include <Locker.h> 24 25#include <package/manager/Exceptions.h> 26#include <package/hpkg/NoErrorOutput.h> 27#include <package/hpkg/PackageContentHandler.h> 28#include <package/hpkg/PackageEntry.h> 29#include <package/hpkg/PackageEntryAttribute.h> 30#include <package/hpkg/PackageReader.h> 31#include <package/solver/SolverPackage.h> 32 33#include "AppUtils.h" 34#include "Logger.h" 35#include "PackageManager.h" 36 37 38#undef B_TRANSLATION_CONTEXT 39#define B_TRANSLATION_CONTEXT "InstallPackageProcess" 40 41using namespace BPackageKit; 42using namespace BPackageKit::BPrivate; 43using namespace BPackageKit::BManager::BPrivate; 44 45using BPackageKit::BSolver; 46using BPackageKit::BSolverPackage; 47using BPackageKit::BSolverRepository; 48using BPackageKit::BHPKG::BNoErrorOutput; 49using BPackageKit::BHPKG::BPackageContentHandler; 50using BPackageKit::BHPKG::BPackageEntry; 51using BPackageKit::BHPKG::BPackageEntryAttribute; 52using BPackageKit::BHPKG::BPackageInfoAttributeValue; 53using BPackageKit::BHPKG::BPackageReader; 54 55 56class DownloadProgress { 57public: 58 DownloadProgress(BString packageName, float progress) 59 : 60 fPackageName(packageName), 61 fProgress(progress) 62 { 63 } 64 65 virtual ~DownloadProgress() 66 { 67 } 68 69 BString PackageName() const 70 { 71 return fPackageName; 72 } 73 74 float Progress() const 75 { 76 return fProgress; 77 } 78 79private: 80 BString fPackageName; 81 float fProgress; 82}; 83 84 85InstallPackageProcess::InstallPackageProcess( 86 PackageInfoRef package, Model* model) 87 : 88 AbstractPackageProcess(package, model), 89 fLastDownloadUpdate(0) 90{ 91 fDescription = B_TRANSLATE("Installing \"%PackageName%\""); 92 fDescription.ReplaceAll("%PackageName%", package->Name()); 93} 94 95 96InstallPackageProcess::~InstallPackageProcess() 97{ 98} 99 100 101const char* 102InstallPackageProcess::Name() const 103{ 104 return "InstallPackageProcess"; 105} 106 107 108const char* 109InstallPackageProcess::Description() const 110{ 111 return fDescription; 112} 113 114 115float 116InstallPackageProcess::Progress() 117{ 118 if (ProcessState() == PROCESS_RUNNING && !fDownloadProgresses.empty()) { 119 AutoLocker<BLocker> locker(&fLock); 120 float sum = 0.0; 121 std::vector<DownloadProgress>::const_iterator it; 122 for (it = fDownloadProgresses.begin(); 123 it != fDownloadProgresses.end(); it++) { 124 DownloadProgress downloadProgress = *it; 125 sum += downloadProgress.Progress(); 126 } 127 if (sum > 0.0) 128 return sum / (float) fDownloadProgresses.size(); 129 } 130 return kProgressIndeterminate; 131} 132 133 134status_t 135InstallPackageProcess::RunInternal() 136{ 137 fPackageManager->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES 138 | BPackageManager::B_ADD_REMOTE_REPOSITORIES 139 | BPackageManager::B_REFRESH_REPOSITORIES); 140 PackageInfoRef ref(fPackage); 141 PackageState state = ref->State(); 142 ref->SetState(PENDING); 143 144 fPackageManager->SetCurrentActionPackage(ref, true); 145 fPackageManager->AddProgressListener(this); 146 147 BString packageName; 148 if (ref->IsLocalFile()) 149 packageName = ref->LocalFilePath(); 150 else 151 packageName = ref->Name(); 152 153 const char* packageNameString = packageName.String(); 154 try { 155 fPackageManager->Install(&packageNameString, 1); 156 } catch (BFatalErrorException& ex) { 157 BString errorString; 158 errorString.SetToFormat( 159 "Fatal error occurred while installing package %s: " 160 "%s (%s)\n", packageNameString, ex.Message().String(), 161 ex.Details().String()); 162 AppUtils::NotifySimpleError(B_TRANSLATE("Fatal error"), errorString, 163 B_STOP_ALERT); 164 _SetDownloadedPackagesState(NONE); 165 ref->SetState(state); 166 return ex.Error(); 167 } catch (BAbortedByUserException& ex) { 168 HDINFO("Installation of package %s is aborted by user: %s", 169 packageNameString, ex.Message().String()); 170 _SetDownloadedPackagesState(NONE); 171 ref->SetState(state); 172 return B_OK; 173 } catch (BNothingToDoException& ex) { 174 HDINFO("Nothing to do while installing package %s: %s", 175 packageNameString, ex.Message().String()); 176 return B_OK; 177 } catch (BException& ex) { 178 HDERROR("Exception occurred while installing package %s: %s", 179 packageNameString, ex.Message().String()); 180 _SetDownloadedPackagesState(NONE); 181 ref->SetState(state); 182 return B_ERROR; 183 } 184 185 fPackageManager->RemoveProgressListener(this); 186 187 _SetDownloadedPackagesState(ACTIVATED); 188 189 return B_OK; 190} 191 192 193// #pragma mark - DownloadProgressListener 194 195 196void 197InstallPackageProcess::DownloadProgressChanged( 198 const char* packageName, float progress) 199{ 200 bigtime_t now = system_time(); 201 if (now - fLastDownloadUpdate < 250000 && progress != 1.0) 202 return; 203 fLastDownloadUpdate = now; 204 BString simplePackageName; 205 if (_DeriveSimplePackageName(packageName, simplePackageName) != B_OK) { 206 HDERROR("malformed canonical package name [%s]", packageName); 207 return; 208 } 209 PackageInfoRef ref(FindPackageByName(simplePackageName)); 210 if (ref.IsSet()) { 211 ref->SetDownloadProgress(progress); 212 _SetDownloadProgress(simplePackageName, progress); 213 } else { 214 HDERROR("unable to find the package info for simple package name [%s]", 215 simplePackageName.String()); 216 } 217} 218 219 220void 221InstallPackageProcess::DownloadProgressComplete(const char* packageName) 222{ 223 BString simplePackageName; 224 if (_DeriveSimplePackageName(packageName, simplePackageName) != B_OK) { 225 HDERROR("malformed canonical package name [%s]", packageName); 226 return; 227 } 228 _SetDownloadProgress(simplePackageName, 1.0); 229 PackageInfoRef ref(FindPackageByName(simplePackageName)); 230 if (!ref.IsSet()) { 231 HDERROR("unable to find the package info for simple package name [%s]", 232 simplePackageName.String()); 233 return; 234 } 235 ref->SetDownloadProgress(1.0); 236 fDownloadedPackages.insert(ref); 237} 238 239 240void 241InstallPackageProcess::ConfirmedChanges( 242 BPackageManager::InstalledRepository& repository) 243{ 244 BPackageManager::InstalledRepository::PackageList& activationList = 245 repository.PackagesToActivate(); 246 247 BSolverPackage* package = NULL; 248 for (int32 i = 0; (package = activationList.ItemAt(i)); i++) { 249 PackageInfoRef ref(FindPackageByName(package->Info().Name())); 250 if (ref.IsSet()) 251 ref->SetState(PENDING); 252 } 253} 254 255 256void 257InstallPackageProcess::_SetDownloadedPackagesState(PackageState state) 258{ 259 for (PackageInfoSet::iterator it = fDownloadedPackages.begin(); 260 it != fDownloadedPackages.end(); ++it) { 261 (*it)->SetState(state); 262 } 263} 264 265 266static bool 267_IsDownloadProgressBefore(const DownloadProgress& dp1, 268 const DownloadProgress& dp2) 269{ 270 return dp1.PackageName().Compare(dp2.PackageName()) < 0; 271} 272 273 274/*! This method will extract the plain package name from the canonical 275 */ 276 277/*static*/ status_t 278InstallPackageProcess::_DeriveSimplePackageName(const BString& canonicalForm, 279 BString& simplePackageName) 280{ 281 int32 hypenIndex = canonicalForm.FindFirst('-'); 282 if (hypenIndex <= 0) 283 return B_BAD_DATA; 284 simplePackageName.SetTo(canonicalForm); 285 simplePackageName.Truncate(hypenIndex); 286 return B_OK; 287} 288 289 290void 291InstallPackageProcess::_SetDownloadProgress(const BString& simplePackageName, 292 float progress) 293{ 294 AutoLocker<BLocker> locker(&fLock); 295 DownloadProgress downloadProgress(simplePackageName, progress); 296 std::vector<DownloadProgress>::iterator itInsertionPt 297 = std::lower_bound( 298 fDownloadProgresses.begin(), fDownloadProgresses.end(), 299 downloadProgress, &_IsDownloadProgressBefore); 300 301 if (itInsertionPt != fDownloadProgresses.end()) { 302 if ((*itInsertionPt).PackageName() == simplePackageName) { 303 itInsertionPt = fDownloadProgresses.erase(itInsertionPt); 304 } 305 } 306 307 fDownloadProgresses.insert(itInsertionPt, downloadProgress); 308 _NotifyChanged(); 309} 310