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