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