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