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 41 using namespace BPackageKit; 42 using namespace BPackageKit::BPrivate; 43 using namespace BPackageKit::BManager::BPrivate; 44 45 using BPackageKit::BSolver; 46 using BPackageKit::BSolverPackage; 47 using BPackageKit::BSolverRepository; 48 using BPackageKit::BHPKG::BNoErrorOutput; 49 using BPackageKit::BHPKG::BPackageContentHandler; 50 using BPackageKit::BHPKG::BPackageEntry; 51 using BPackageKit::BHPKG::BPackageEntryAttribute; 52 using BPackageKit::BHPKG::BPackageInfoAttributeValue; 53 using BPackageKit::BHPKG::BPackageReader; 54 55 56 class DownloadProgress { 57 public: 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 79 private: 80 BString fPackageName; 81 float fProgress; 82 }; 83 84 85 InstallPackageProcess::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 96 InstallPackageProcess::~InstallPackageProcess() 97 { 98 } 99 100 101 const char* 102 InstallPackageProcess::Name() const 103 { 104 return "InstallPackageProcess"; 105 } 106 107 108 const char* 109 InstallPackageProcess::Description() const 110 { 111 return fDescription; 112 } 113 114 115 float 116 InstallPackageProcess::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 134 status_t 135 InstallPackageProcess::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 196 void 197 InstallPackageProcess::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 220 void 221 InstallPackageProcess::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 240 void 241 InstallPackageProcess::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 256 void 257 InstallPackageProcess::_SetDownloadedPackagesState(PackageState state) 258 { 259 for (PackageInfoSet::iterator it = fDownloadedPackages.begin(); 260 it != fDownloadedPackages.end(); ++it) { 261 (*it)->SetState(state); 262 } 263 } 264 265 266 static 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 278 InstallPackageProcess::_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 290 void 291 InstallPackageProcess::_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