1 /* 2 * Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "PackageManager.h" 8 9 #include <Notification.h> 10 #include <package/DownloadFileRequest.h> 11 #include <package/RefreshRepositoryRequest.h> 12 #include <package/solver/SolverPackage.h> 13 #include <package/solver/SolverPackageSpecifierList.h> 14 #include <package/solver/SolverProblem.h> 15 #include <package/solver/SolverProblemSolution.h> 16 17 #include <AutoDeleter.h> 18 #include <package/manager/Exceptions.h> 19 #include <package/manager/RepositoryBuilder.h> 20 #include <Server.h> 21 22 #include "ProblemWindow.h" 23 #include "ResultWindow.h" 24 #include "Root.h" 25 #include "Volume.h" 26 27 28 using BPackageKit::BManager::BPrivate::BAbortedByUserException; 29 using BPackageKit::BManager::BPrivate::BFatalErrorException; 30 using BPackageKit::BManager::BPrivate::BRepositoryBuilder; 31 32 33 PackageManager::PackageManager(Root* root, Volume* volume) 34 : 35 BPackageManager(volume->Location(), this, this), 36 BPackageManager::UserInteractionHandler(), 37 fRoot(root), 38 fVolume(volume), 39 fSolverPackages(), 40 fPackagesAddedByUser(), 41 fPackagesRemovedByUser(), 42 fProblemWindow(NULL) 43 { 44 } 45 46 47 PackageManager::~PackageManager() 48 { 49 if (fProblemWindow != NULL) 50 fProblemWindow->PostMessage(B_QUIT_REQUESTED); 51 } 52 53 54 void 55 PackageManager::HandleUserChanges() 56 { 57 const PackageSet& packagesToActivate = fVolume->PackagesToBeActivated(); 58 const PackageSet& packagesToDeactivate = fVolume->PackagesToBeDeactivated(); 59 60 if (packagesToActivate.empty() && packagesToDeactivate.empty()) 61 return; 62 63 if (packagesToActivate.empty()) { 64 // only packages removed -- use uninstall mode 65 Init(B_ADD_INSTALLED_REPOSITORIES); 66 67 BSolverPackageSpecifierList packagesToUninstall; 68 for (PackageSet::const_iterator it = packagesToDeactivate.begin(); 69 it != packagesToDeactivate.end(); ++it) { 70 BSolverPackage* solverPackage = _SolverPackageFor(*it); 71 if (solverPackage == NULL) 72 continue; 73 74 if (!packagesToUninstall.AppendSpecifier(solverPackage)) 75 throw std::bad_alloc(); 76 fPackagesRemovedByUser.insert(solverPackage); 77 } 78 79 if (fPackagesRemovedByUser.empty()) 80 return; 81 82 Uninstall(packagesToUninstall); 83 } else { 84 // packages added and (possibly) remove -- manually add/remove those 85 // from the repository and use verify mode 86 Init(B_ADD_INSTALLED_REPOSITORIES | B_ADD_REMOTE_REPOSITORIES 87 | B_REFRESH_REPOSITORIES); 88 89 // disable and remove uninstalled packages 90 InstalledRepository& repository = InstallationRepository(); 91 for (PackageSet::const_iterator it = packagesToDeactivate.begin(); 92 it != packagesToDeactivate.end(); ++it) { 93 BSolverPackage* solverPackage = _SolverPackageFor(*it); 94 if (solverPackage == NULL) 95 continue; 96 97 repository.DisablePackage(solverPackage); 98 if (!repository.PackagesToDeactivate().AddItem(solverPackage)) 99 throw std::bad_alloc(); 100 fPackagesRemovedByUser.insert(solverPackage); 101 } 102 103 // add new packages 104 BRepositoryBuilder repositoryBuilder(repository); 105 for (PackageSet::const_iterator it = packagesToActivate.begin(); 106 it != packagesToActivate.end(); ++it) { 107 Package* package = *it; 108 BSolverPackage* solverPackage; 109 repositoryBuilder.AddPackage(package->Info(), NULL, &solverPackage); 110 fSolverPackages[package] = solverPackage; 111 if (!repository.PackagesToActivate().AddItem(solverPackage)) 112 throw std::bad_alloc(); 113 fPackagesAddedByUser.insert(solverPackage); 114 } 115 116 if (fPackagesRemovedByUser.empty() && fPackagesAddedByUser.empty()) 117 return; 118 119 // TODO: When packages are moved out of the packages directory, we can't create 120 // a complete "old-state" directory. 121 122 VerifyInstallation(); 123 } 124 } 125 126 127 void 128 PackageManager::InitInstalledRepository(InstalledRepository& repository) 129 { 130 const char* name = repository.InitialName(); 131 BRepositoryBuilder repositoryBuilder(repository, name); 132 133 if (Volume* volume = fRoot->GetVolume(repository.Location())) { 134 for (PackageFileNameHashTable::Iterator it 135 = volume->PackagesByFileNameIterator(); it.HasNext();) { 136 Package* package = it.Next(); 137 if (package->IsActive()) { 138 BSolverPackage* solverPackage; 139 repositoryBuilder.AddPackage(package->Info(), NULL, 140 &solverPackage); 141 fSolverPackages[package] = solverPackage; 142 } 143 } 144 } 145 } 146 147 148 void 149 PackageManager::ResultComputed(InstalledRepository& repository) 150 { 151 // Normalize the result, i.e. remove the packages added by the user which 152 // have been removed again in the problem resolution process, and vice 153 // versa. 154 if (repository.Location() != fVolume->Location()) 155 return; 156 157 PackageList& packagesToActivate = repository.PackagesToActivate(); 158 PackageList& packagesToDeactivate = repository.PackagesToDeactivate(); 159 160 for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i); 161 i++) { 162 if (fPackagesAddedByUser.erase(package) == 0) 163 continue; 164 165 for (SolverPackageMap::iterator it = fSolverPackages.begin(); 166 it != fSolverPackages.end(); ++it) { 167 if (it->second == package) { 168 fSolverPackages.erase(it); 169 break; 170 } 171 } 172 173 repository.EnablePackage(package); 174 packagesToDeactivate.RemoveItemAt(i--); 175 packagesToActivate.RemoveItem(package); 176 repository.DeletePackage(package); 177 } 178 179 for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i); 180 i++) { 181 if (fPackagesRemovedByUser.erase(package) == 0) 182 continue; 183 184 repository.EnablePackage(package); 185 packagesToActivate.RemoveItemAt(i--); 186 packagesToDeactivate.RemoveItem(package); 187 188 // Note: We keep the package activated, but nonetheless it is gone, 189 // since the user has removed it from the packages directory. So unless 190 // the user moves it back, we won't find it upon next reboot. 191 // TODO: We probable even run into trouble when the user moves in a 192 // replacement afterward. 193 } 194 } 195 196 197 status_t 198 PackageManager::PrepareTransaction(Transaction& transaction) 199 { 200 Volume* volume = fRoot->GetVolume(transaction.Repository().Location()); 201 if (volume == NULL) 202 return B_BAD_VALUE; 203 204 return volume->CreateTransaction(transaction.Repository().Location(), 205 transaction.ActivationTransaction(), 206 transaction.TransactionDirectory()); 207 } 208 209 210 status_t 211 PackageManager::CommitTransaction(Transaction& transaction, 212 BCommitTransactionResult& _result) 213 { 214 Volume* volume = fRoot->GetVolume(transaction.Repository().Location()); 215 if (volume == NULL) 216 return B_BAD_VALUE; 217 218 // Get the packages that have already been added to/removed from the 219 // packages directory and thus need to be handled specially by 220 // Volume::CommitTransaction(). 221 PackageSet packagesAlreadyAdded; 222 PackageSet packagesAlreadyRemoved; 223 if (volume == fVolume) { 224 const PackageSet& packagesToActivate = volume->PackagesToBeActivated(); 225 for (PackageSet::const_iterator it = packagesToActivate.begin(); 226 it != packagesToActivate.end(); ++it) { 227 Package* package = *it; 228 if (fPackagesAddedByUser.find(_SolverPackageFor(package)) 229 != fPackagesAddedByUser.end()) { 230 packagesAlreadyAdded.insert(package); 231 } 232 } 233 234 const PackageSet& packagesToDeactivate 235 = volume->PackagesToBeDeactivated(); 236 for (PackageSet::const_iterator it = packagesToDeactivate.begin(); 237 it != packagesToDeactivate.end(); ++it) { 238 Package* package = *it; 239 if (fPackagesRemovedByUser.find(_SolverPackageFor(package)) 240 != fPackagesRemovedByUser.end()) { 241 packagesAlreadyRemoved.insert(package); 242 } 243 } 244 } 245 246 volume->CommitTransaction(transaction.ActivationTransaction(), 247 packagesAlreadyAdded, packagesAlreadyRemoved, _result); 248 return B_OK; 249 } 250 251 252 void 253 PackageManager::HandleProblems() 254 { 255 _InitGui(); 256 257 if (fProblemWindow == NULL) 258 fProblemWindow = new ProblemWindow; 259 260 if (!fProblemWindow->Go(fSolver, fPackagesAddedByUser, 261 fPackagesRemovedByUser)) { 262 throw BAbortedByUserException(); 263 } 264 } 265 266 267 void 268 PackageManager::ConfirmChanges(bool fromMostSpecific) 269 { 270 // Check whether there are any changes other than those made by the user. 271 _InitGui(); 272 ResultWindow* window = new ResultWindow; 273 ObjectDeleter<ResultWindow> windowDeleter(window); 274 275 bool hasOtherChanges = false; 276 int32 count = fInstalledRepositories.CountItems(); 277 if (fromMostSpecific) { 278 for (int32 i = count - 1; i >= 0; i--) 279 hasOtherChanges 280 |= _AddResults(*fInstalledRepositories.ItemAt(i), window); 281 } else { 282 for (int32 i = 0; i < count; i++) 283 hasOtherChanges 284 |= _AddResults(*fInstalledRepositories.ItemAt(i), window); 285 } 286 287 if (!hasOtherChanges) 288 return; 289 290 // show the window 291 if (windowDeleter.Detach()->Go() == 0) 292 throw BAbortedByUserException(); 293 } 294 295 296 void 297 PackageManager::Warn(status_t error, const char* format, ...) 298 { 299 va_list args; 300 va_start(args, format); 301 BString message; 302 message.SetToFormatVarArgs(format, args); 303 va_end(args); 304 305 if (error != B_OK) 306 message << BString().SetToFormat(": %s", strerror(error)); 307 308 BNotification notification(B_ERROR_NOTIFICATION); 309 notification.SetTitle("Package Daemon"); 310 notification.SetContent(message); 311 notification.Send(); 312 } 313 314 315 void 316 PackageManager::ProgressPackageDownloadStarted(const char* packageName) 317 { 318 } 319 320 321 void 322 PackageManager::ProgressPackageDownloadActive(const char* packageName, 323 float completionPercentage) 324 { 325 } 326 327 328 void 329 PackageManager::ProgressPackageDownloadComplete(const char* packageName) 330 { 331 } 332 333 334 void 335 PackageManager::ProgressPackageChecksumStarted(const char* title) 336 { 337 } 338 339 340 void 341 PackageManager::ProgressPackageChecksumComplete(const char* title) 342 { 343 } 344 345 346 void 347 PackageManager::ProgressStartApplyingChanges(InstalledRepository& repository) 348 { 349 } 350 351 352 void 353 PackageManager::ProgressTransactionCommitted(InstalledRepository& repository, 354 const BCommitTransactionResult& result) 355 { 356 } 357 358 359 void 360 PackageManager::ProgressApplyingChangesDone(InstalledRepository& repository) 361 { 362 } 363 364 365 void 366 PackageManager::JobFailed(BJob* job) 367 { 368 // TODO:... 369 } 370 371 372 void 373 PackageManager::JobAborted(BJob* job) 374 { 375 // TODO:... 376 } 377 378 379 bool 380 PackageManager::_AddResults(InstalledRepository& repository, 381 ResultWindow* window) 382 { 383 if (!repository.HasChanges()) 384 return false; 385 386 return window->AddLocationChanges(repository.Name(), 387 repository.PackagesToActivate(), fPackagesAddedByUser, 388 repository.PackagesToDeactivate(), fPackagesRemovedByUser); 389 } 390 391 392 BSolverPackage* 393 PackageManager::_SolverPackageFor(Package* package) const 394 { 395 SolverPackageMap::const_iterator it = fSolverPackages.find(package); 396 return it != fSolverPackages.end() ? it->second : NULL; 397 } 398 399 400 void 401 PackageManager::_InitGui() 402 { 403 BServer* server = dynamic_cast<BServer*>(be_app); 404 if (server == NULL || server->InitGUIContext() != B_OK) 405 throw BFatalErrorException("failed to initialize the GUI"); 406 } 407