1 /* 2 * Copyright 2013-2017, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler <axeld@pinc-software.de> 7 * Rene Gollent <rene@gollent.com> 8 * Ingo Weinhold <ingo_weinhold@gmx.de> 9 * Brian Hill <supernova@tycho.email> 10 */ 11 12 13 #include "UpdateManager.h" 14 15 #include <sys/ioctl.h> 16 #include <unistd.h> 17 18 #include <Alert.h> 19 #include <Application.h> 20 #include <Catalog.h> 21 #include <Message.h> 22 #include <Messenger.h> 23 #include <NetworkInterface.h> 24 #include <NetworkRoster.h> 25 #include <Notification.h> 26 #include <Roster.h> 27 28 #include <package/manager/Exceptions.h> 29 #include <package/solver/SolverPackage.h> 30 31 #include "constants.h" 32 #include "ProblemWindow.h" 33 34 using namespace BPackageKit; 35 using namespace BPackageKit::BManager::BPrivate; 36 37 #undef B_TRANSLATION_CONTEXT 38 #define B_TRANSLATION_CONTEXT "UpdateManager" 39 40 41 UpdateManager::UpdateManager(BPackageInstallationLocation location, 42 bool verbose) 43 : 44 BPackageManager(location, &fClientInstallationInterface, this), 45 BPackageManager::UserInteractionHandler(), 46 fClientInstallationInterface(), 47 fStatusWindow(NULL), 48 fCurrentStep(ACTION_STEP_INIT), 49 fChangesConfirmed(false), 50 fVerbose(verbose) 51 { 52 fStatusWindow = new SoftwareUpdaterWindow(); 53 _SetCurrentStep(ACTION_STEP_START); 54 } 55 56 57 UpdateManager::~UpdateManager() 58 { 59 if (fStatusWindow != NULL) { 60 fStatusWindow->Lock(); 61 fStatusWindow->Quit(); 62 } 63 if (fProblemWindow != NULL) { 64 fProblemWindow->Lock(); 65 fProblemWindow->Quit(); 66 } 67 } 68 69 70 void 71 UpdateManager::CheckNetworkConnection() 72 { 73 BNetworkRoster& roster = BNetworkRoster::Default(); 74 BNetworkInterface interface; 75 uint32 cookie = 0; 76 while (roster.GetNextInterface(&cookie, interface) == B_OK) { 77 uint32 flags = interface.Flags(); 78 if ((flags & IFF_LOOPBACK) == 0 79 && (flags & (IFF_UP | IFF_LINK)) == (IFF_UP | IFF_LINK)) { 80 return; 81 } 82 } 83 84 // No network connection detected, cannot continue 85 throw BException(B_TRANSLATE_COMMENT( 86 "No active network connection was found", "Error message")); 87 } 88 89 90 update_type 91 UpdateManager::GetUpdateType() 92 { 93 int32 action = USER_SELECTION_NEEDED; 94 BMessenger messenger(fStatusWindow); 95 if (messenger.IsValid()) { 96 BMessage message(kMsgGetUpdateType); 97 BMessage reply; 98 messenger.SendMessage(&message, &reply); 99 reply.FindInt32(kKeyAlertResult, &action); 100 } 101 return (update_type)action; 102 } 103 104 105 void 106 UpdateManager::CheckRepositories() 107 { 108 int32 count = fOtherRepositories.CountItems(); 109 if (fVerbose) 110 printf("Remote repositories available: %" B_PRId32 "\n", count); 111 if (count == 0) { 112 BMessenger messenger(fStatusWindow); 113 if (messenger.IsValid()) { 114 BMessage message(kMsgNoRepositories); 115 BMessage reply; 116 messenger.SendMessage(&message, &reply); 117 int32 result; 118 reply.FindInt32(kKeyAlertResult, &result); 119 if (result == 1) 120 be_roster->Launch("application/x-vnd.Haiku-Repositories"); 121 } 122 be_app->PostMessage(kMsgFinalQuit); 123 throw BException(B_TRANSLATE_COMMENT( 124 "No remote repositories are available", "Error message")); 125 } 126 } 127 128 129 void 130 UpdateManager::JobFailed(BSupportKit::BJob* job) 131 { 132 if (!fVerbose) 133 return; 134 135 BString error = job->ErrorString(); 136 if (error.Length() > 0) { 137 error.ReplaceAll("\n", "\n*** "); 138 fprintf(stderr, "%s", error.String()); 139 } 140 } 141 142 143 void 144 UpdateManager::JobAborted(BSupportKit::BJob* job) 145 { 146 if (fVerbose) 147 puts("Job aborted"); 148 } 149 150 151 void 152 UpdateManager::FinalUpdate(const char* header, const char* text) 153 { 154 _FinalUpdate(header, text); 155 } 156 157 158 void 159 UpdateManager::HandleProblems() 160 { 161 if (fProblemWindow == NULL) 162 fProblemWindow = new ProblemWindow; 163 164 ProblemWindow::SolverPackageSet installPackages; 165 ProblemWindow::SolverPackageSet uninstallPackages; 166 if (!fProblemWindow->Go(fSolver,installPackages, uninstallPackages)) 167 throw BAbortedByUserException(); 168 fProblemWindow->Hide(); 169 } 170 171 172 void 173 UpdateManager::ConfirmChanges(bool fromMostSpecific) 174 { 175 if (fVerbose) 176 puts("The following changes will be made:"); 177 178 int32 count = fInstalledRepositories.CountItems(); 179 int32 upgradeCount = 0; 180 int32 installCount = 0; 181 int32 uninstallCount = 0; 182 183 if (fromMostSpecific) { 184 for (int32 i = count - 1; i >= 0; i--) 185 _PrintResult(*fInstalledRepositories.ItemAt(i), upgradeCount, 186 installCount, uninstallCount); 187 } else { 188 for (int32 i = 0; i < count; i++) 189 _PrintResult(*fInstalledRepositories.ItemAt(i), upgradeCount, 190 installCount, uninstallCount); 191 } 192 193 if (fVerbose) 194 printf("Upgrade count=%" B_PRId32 ", Install count=%" B_PRId32 195 ", Uninstall count=%" B_PRId32 "\n", 196 upgradeCount, installCount, uninstallCount); 197 198 fChangesConfirmed = fStatusWindow->ConfirmUpdates(); 199 if (!fChangesConfirmed) 200 throw BAbortedByUserException(); 201 202 _SetCurrentStep(ACTION_STEP_DOWNLOAD); 203 fPackageDownloadsTotal = upgradeCount + installCount; 204 fPackageDownloadsCount = 1; 205 } 206 207 208 void 209 UpdateManager::Warn(status_t error, const char* format, ...) 210 { 211 char buffer[256]; 212 va_list args; 213 va_start(args, format); 214 vsnprintf(buffer, sizeof(buffer), format, args); 215 va_end(args); 216 217 if (fVerbose) { 218 fputs(buffer, stderr); 219 if (error == B_OK) 220 puts(""); 221 else 222 printf(": %s\n", strerror(error)); 223 } 224 225 if (fStatusWindow != NULL) { 226 if (fStatusWindow->UserCancelRequested()) 227 throw BAbortedByUserException(); 228 fStatusWindow->ShowWarningAlert(buffer); 229 } else { 230 BString text("SoftwareUpdater:\n"); 231 text.Append(buffer); 232 BAlert* alert = new BAlert("warning", text, B_TRANSLATE("OK"), NULL, 233 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 234 alert->Go(NULL); 235 } 236 } 237 238 239 void 240 UpdateManager::ProgressPackageDownloadStarted(const char* packageName) 241 { 242 if (fCurrentStep == ACTION_STEP_DOWNLOAD) { 243 BString header(B_TRANSLATE("Downloading packages")); 244 _UpdateDownloadProgress(header.String(), packageName, 0.0); 245 fNewDownloadStarted = false; 246 } 247 248 if (fVerbose) 249 printf("Downloading %s...\n", packageName); 250 } 251 252 253 void 254 UpdateManager::ProgressPackageDownloadActive(const char* packageName, 255 float completionValue, off_t bytes, off_t totalBytes) 256 { 257 if (fCurrentStep == ACTION_STEP_DOWNLOAD) { 258 // Fix a bug where a 100% completion percentage gets sent at the start 259 // of a package download 260 if (!fNewDownloadStarted) { 261 if (completionValue > 0 && completionValue < 1) 262 fNewDownloadStarted = true; 263 else 264 completionValue = 0.0; 265 } 266 _UpdateDownloadProgress(NULL, packageName, completionValue * 100.0); 267 } 268 269 if (fVerbose) { 270 static const char* progressChars[] = { 271 "\xE2\x96\x8F", 272 "\xE2\x96\x8E", 273 "\xE2\x96\x8D", 274 "\xE2\x96\x8C", 275 "\xE2\x96\x8B", 276 "\xE2\x96\x8A", 277 "\xE2\x96\x89", 278 "\xE2\x96\x88", 279 }; 280 281 int width = 70; 282 283 struct winsize winSize; 284 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winSize) == 0 285 && winSize.ws_col < 77) { 286 // We need 7 characters for the percent display 287 width = winSize.ws_col - 7; 288 } 289 290 int position; 291 int ipart = (int)(completionValue * width); 292 int fpart = (int)(((completionValue * width) - ipart) * 8); 293 294 fputs("\r", stdout); // return to the beginning of the line 295 296 for (position = 0; position < width; position++) { 297 if (position < ipart) { 298 // This part is fully downloaded, show a full block 299 fputs(progressChars[7], stdout); 300 } else if (position > ipart) { 301 // This part is not downloaded, show a space 302 fputs(" ", stdout); 303 } else { 304 // This part is partially downloaded 305 fputs(progressChars[fpart], stdout); 306 } 307 } 308 309 // Also print the progress percentage 310 printf(" %3d%%", (int)(completionValue * 100)); 311 312 fflush(stdout); 313 } 314 315 } 316 317 318 void 319 UpdateManager::ProgressPackageDownloadComplete(const char* packageName) 320 { 321 if (fCurrentStep == ACTION_STEP_DOWNLOAD) { 322 _UpdateDownloadProgress(NULL, packageName, 100.0); 323 fPackageDownloadsCount++; 324 } 325 326 if (fVerbose) { 327 // Overwrite the progress bar with whitespace 328 fputs("\r", stdout); 329 struct winsize w; 330 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); 331 for (int i = 0; i < (w.ws_col); i++) 332 fputs(" ", stdout); 333 fputs("\r\x1b[1A", stdout); // Go to previous line. 334 335 printf("Downloading %s...done.\n", packageName); 336 } 337 } 338 339 340 void 341 UpdateManager::ProgressPackageChecksumStarted(const char* title) 342 { 343 // Repository checksums 344 if (fCurrentStep == ACTION_STEP_START) 345 _UpdateStatusWindow(NULL, title); 346 347 if (fVerbose) 348 printf("%s...", title); 349 } 350 351 352 void 353 UpdateManager::ProgressPackageChecksumComplete(const char* title) 354 { 355 if (fVerbose) 356 puts("done."); 357 } 358 359 360 void 361 UpdateManager::ProgressStartApplyingChanges(InstalledRepository& repository) 362 { 363 _SetCurrentStep(ACTION_STEP_APPLY); 364 BString header(B_TRANSLATE("Applying changes")); 365 BString detail(B_TRANSLATE("Packages are being updated")); 366 fStatusWindow->UpdatesApplying(header.String(), detail.String()); 367 368 if (fVerbose) 369 printf("[%s] Applying changes ...\n", repository.Name().String()); 370 } 371 372 373 void 374 UpdateManager::ProgressTransactionCommitted(InstalledRepository& repository, 375 const BCommitTransactionResult& result) 376 { 377 _SetCurrentStep(ACTION_STEP_COMPLETE); 378 BString header(B_TRANSLATE("Updates completed")); 379 BString detail(B_TRANSLATE("A reboot may be necessary to complete some " 380 "updates.")); 381 _FinalUpdate(header.String(), detail.String()); 382 383 if (fVerbose) { 384 const char* repositoryName = repository.Name().String(); 385 386 int32 issueCount = result.CountIssues(); 387 for (int32 i = 0; i < issueCount; i++) { 388 const BTransactionIssue* issue = result.IssueAt(i); 389 if (issue->PackageName().IsEmpty()) { 390 printf("[%s] warning: %s\n", repositoryName, 391 issue->ToString().String()); 392 } else { 393 printf("[%s] warning: package %s: %s\n", repositoryName, 394 issue->PackageName().String(), issue->ToString().String()); 395 } 396 } 397 398 printf("[%s] Changes applied. Old activation state backed up in \"%s\"\n", 399 repositoryName, result.OldStateDirectory().String()); 400 printf("[%s] Cleaning up ...\n", repositoryName); 401 } 402 } 403 404 405 void 406 UpdateManager::ProgressApplyingChangesDone(InstalledRepository& repository) 407 { 408 if (fVerbose) 409 printf("[%s] Done.\n", repository.Name().String()); 410 } 411 412 413 void 414 UpdateManager::_PrintResult(InstalledRepository& installationRepository, 415 int32& upgradeCount, int32& installCount, int32& uninstallCount) 416 { 417 if (!installationRepository.HasChanges()) 418 return; 419 420 if (fVerbose) 421 printf(" in %s:\n", installationRepository.Name().String()); 422 423 PackageList& packagesToActivate 424 = installationRepository.PackagesToActivate(); 425 PackageList& packagesToDeactivate 426 = installationRepository.PackagesToDeactivate(); 427 428 BStringList upgradedPackages; 429 BStringList upgradedPackageVersions; 430 for (int32 i = 0; 431 BSolverPackage* installPackage = packagesToActivate.ItemAt(i); 432 i++) { 433 for (int32 j = 0; 434 BSolverPackage* uninstallPackage = packagesToDeactivate.ItemAt(j); 435 j++) { 436 if (installPackage->Info().Name() == uninstallPackage->Info().Name()) { 437 upgradedPackages.Add(installPackage->Info().Name()); 438 upgradedPackageVersions.Add(uninstallPackage->Info().Version().ToString()); 439 break; 440 } 441 } 442 } 443 444 for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i); 445 i++) { 446 BString repository; 447 if (dynamic_cast<MiscLocalRepository*>(package->Repository()) != NULL) 448 repository = "local file"; 449 else 450 repository.SetToFormat("repository %s", 451 package->Repository()->Name().String()); 452 453 int position = upgradedPackages.IndexOf(package->Info().Name()); 454 if (position >= 0) { 455 if (fVerbose) 456 printf(" upgrade package %s-%s to %s from %s\n", 457 package->Info().Name().String(), 458 upgradedPackageVersions.StringAt(position).String(), 459 package->Info().Version().ToString().String(), 460 repository.String()); 461 fStatusWindow->AddPackageInfo(PACKAGE_UPDATE, 462 package->Info().Name().String(), 463 upgradedPackageVersions.StringAt(position).String(), 464 package->Info().Version().ToString().String(), 465 package->Info().Summary().String(), 466 package->Repository()->Name().String(), 467 package->Info().FileName().String()); 468 upgradeCount++; 469 } else { 470 if (fVerbose) 471 printf(" install package %s-%s from %s\n", 472 package->Info().Name().String(), 473 package->Info().Version().ToString().String(), 474 repository.String()); 475 fStatusWindow->AddPackageInfo(PACKAGE_INSTALL, 476 package->Info().Name().String(), 477 NULL, 478 package->Info().Version().ToString().String(), 479 package->Info().Summary().String(), 480 package->Repository()->Name().String(), 481 package->Info().FileName().String()); 482 installCount++; 483 } 484 } 485 486 BStringList uninstallList; 487 for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i); 488 i++) { 489 if (upgradedPackages.HasString(package->Info().Name())) 490 continue; 491 if (fVerbose) 492 printf(" uninstall package %s\n", 493 package->VersionedName().String()); 494 fStatusWindow->AddPackageInfo(PACKAGE_UNINSTALL, 495 package->Info().Name().String(), 496 package->Info().Version().ToString(), 497 NULL, 498 package->Info().Summary().String(), 499 package->Repository()->Name().String(), 500 package->Info().FileName().String()); 501 uninstallCount++; 502 } 503 } 504 505 506 void 507 UpdateManager::_UpdateStatusWindow(const char* header, const char* detail) 508 { 509 if (header == NULL && detail == NULL) 510 return; 511 512 if (fStatusWindow->UserCancelRequested()) 513 throw BAbortedByUserException(); 514 515 BMessage message(kMsgTextUpdate); 516 if (header != NULL) 517 message.AddString(kKeyHeader, header); 518 if (detail != NULL) 519 message.AddString(kKeyDetail, detail); 520 fStatusWindow->PostMessage(&message); 521 } 522 523 524 void 525 UpdateManager::_UpdateDownloadProgress(const char* header, 526 const char* packageName, float percentageComplete) 527 { 528 if (packageName == NULL) 529 return; 530 531 if (fStatusWindow->UserCancelRequested()) 532 throw BAbortedByUserException(); 533 534 BString packageCount; 535 packageCount.SetToFormat( 536 B_TRANSLATE_COMMENT("%i of %i", "Do not translate %i"), 537 fPackageDownloadsCount, 538 fPackageDownloadsTotal); 539 BMessage message(kMsgProgressUpdate); 540 if (header != NULL) 541 message.AddString(kKeyHeader, header); 542 message.AddString(kKeyPackageName, packageName); 543 message.AddString(kKeyPackageCount, packageCount.String()); 544 message.AddFloat(kKeyPercentage, percentageComplete); 545 fStatusWindow->PostMessage(&message); 546 } 547 548 549 void 550 UpdateManager::_FinalUpdate(const char* header, const char* text) 551 { 552 if (!fStatusWindow->IsFront()) { 553 BNotification notification(B_INFORMATION_NOTIFICATION); 554 notification.SetGroup("SoftwareUpdater"); 555 notification.SetTitle(header); 556 notification.SetContent(text); 557 notification.Send(); 558 } 559 560 fStatusWindow->FinalUpdate(header, text); 561 } 562 563 564 void 565 UpdateManager::_SetCurrentStep(int32 step) 566 { 567 fCurrentStep = step; 568 } 569