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