2 * Copyright 2013-2017, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
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>
13 #include "UpdateManager.h"
15 #include <sys/ioctl.h>
19 #include <Application.h>
22 #include <Messenger.h>
23 #include <NetworkInterface.h>
24 #include <NetworkRoster.h>
25 #include <Notification.h>
28 #include <package/manager/Exceptions.h>
29 #include <package/solver/SolverPackage.h>
31 #include "constants.h"
32 #include "ProblemWindow.h"
34 using namespace BPackageKit
;
35 using namespace BPackageKit::BManager::BPrivate
;
37 #undef B_TRANSLATION_CONTEXT
38 #define B_TRANSLATION_CONTEXT "UpdateManager"
41 UpdateManager::UpdateManager(BPackageInstallationLocation location
,
44 BPackageManager(location
, &fClientInstallationInterface
, this),
45 BPackageManager::UserInteractionHandler(),
46 fClientInstallationInterface(),
48 fCurrentStep(ACTION_STEP_INIT
),
49 fChangesConfirmed(false),
52 fStatusWindow
= new SoftwareUpdaterWindow();
53 _SetCurrentStep(ACTION_STEP_START
);
57 UpdateManager::~UpdateManager()
59 if (fStatusWindow
!= NULL
) {
60 fStatusWindow
->Lock();
61 fStatusWindow
->Quit();
63 if (fProblemWindow
!= NULL
) {
64 fProblemWindow
->Lock();
65 fProblemWindow
->Quit();
71 UpdateManager::CheckNetworkConnection()
73 BNetworkRoster
& roster
= BNetworkRoster::Default();
74 BNetworkInterface interface
;
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
)) {
84 // No network connection detected, cannot continue
85 throw BException(B_TRANSLATE_COMMENT(
86 "No active network connection was found", "Error message"));
91 UpdateManager::GetUpdateType()
93 int32 action
= USER_SELECTION_NEEDED
;
94 BMessenger
messenger(fStatusWindow
);
95 if (messenger
.IsValid()) {
96 BMessage
message(kMsgGetUpdateType
);
98 messenger
.SendMessage(&message
, &reply
);
99 reply
.FindInt32(kKeyAlertResult
, &action
);
101 return (update_type
)action
;
106 UpdateManager::CheckRepositories()
108 int32 count
= fOtherRepositories
.CountItems();
110 printf("Remote repositories available: %" B_PRId32
"\n", count
);
112 BMessenger
messenger(fStatusWindow
);
113 if (messenger
.IsValid()) {
114 BMessage
message(kMsgNoRepositories
);
116 messenger
.SendMessage(&message
, &reply
);
118 reply
.FindInt32(kKeyAlertResult
, &result
);
120 be_roster
->Launch("application/x-vnd.Haiku-Repositories");
122 be_app
->PostMessage(kMsgFinalQuit
);
123 throw BException(B_TRANSLATE_COMMENT(
124 "No remote repositories are available", "Error message"));
130 UpdateManager::JobFailed(BSupportKit::BJob
* job
)
135 BString error
= job
->ErrorString();
136 if (error
.Length() > 0) {
137 error
.ReplaceAll("\n", "\n*** ");
138 fprintf(stderr
, "%s", error
.String());
144 UpdateManager::JobAborted(BSupportKit::BJob
* job
)
152 UpdateManager::FinalUpdate(const char* header
, const char* text
)
154 _FinalUpdate(header
, text
);
159 UpdateManager::HandleProblems()
161 if (fProblemWindow
== NULL
)
162 fProblemWindow
= new ProblemWindow
;
164 ProblemWindow::SolverPackageSet installPackages
;
165 ProblemWindow::SolverPackageSet uninstallPackages
;
166 if (!fProblemWindow
->Go(fSolver
,installPackages
, uninstallPackages
))
167 throw BAbortedByUserException();
168 fProblemWindow
->Hide();
173 UpdateManager::ConfirmChanges(bool fromMostSpecific
)
176 puts("The following changes will be made:");
178 int32 count
= fInstalledRepositories
.CountItems();
179 int32 upgradeCount
= 0;
180 int32 installCount
= 0;
181 int32 uninstallCount
= 0;
183 if (fromMostSpecific
) {
184 for (int32 i
= count
- 1; i
>= 0; i
--)
185 _PrintResult(*fInstalledRepositories
.ItemAt(i
), upgradeCount
,
186 installCount
, uninstallCount
);
188 for (int32 i
= 0; i
< count
; i
++)
189 _PrintResult(*fInstalledRepositories
.ItemAt(i
), upgradeCount
,
190 installCount
, uninstallCount
);
194 printf("Upgrade count=%" B_PRId32
", Install count=%" B_PRId32
195 ", Uninstall count=%" B_PRId32
"\n",
196 upgradeCount
, installCount
, uninstallCount
);
198 fChangesConfirmed
= fStatusWindow
->ConfirmUpdates();
199 if (!fChangesConfirmed
)
200 throw BAbortedByUserException();
202 _SetCurrentStep(ACTION_STEP_DOWNLOAD
);
203 fPackageDownloadsTotal
= upgradeCount
+ installCount
;
204 fPackageDownloadsCount
= 1;
209 UpdateManager::Warn(status_t error
, const char* format
, ...)
213 va_start(args
, format
);
214 vsnprintf(buffer
, sizeof(buffer
), format
, args
);
218 fputs(buffer
, stderr
);
222 printf(": %s\n", strerror(error
));
225 if (fStatusWindow
!= NULL
) {
226 if (fStatusWindow
->UserCancelRequested())
227 throw BAbortedByUserException();
228 fStatusWindow
->ShowWarningAlert(buffer
);
230 BString
text("SoftwareUpdater:\n");
232 BAlert
* alert
= new BAlert("warning", text
, B_TRANSLATE("OK"), NULL
,
233 NULL
, B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
240 UpdateManager::ProgressPackageDownloadStarted(const char* packageName
)
242 if (fCurrentStep
== ACTION_STEP_DOWNLOAD
) {
243 BString
header(B_TRANSLATE("Downloading packages"));
244 _UpdateDownloadProgress(header
.String(), packageName
, 0.0);
245 fNewDownloadStarted
= false;
249 printf("Downloading %s...\n", packageName
);
254 UpdateManager::ProgressPackageDownloadActive(const char* packageName
,
255 float completionValue
, off_t bytes
, off_t totalBytes
)
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;
264 completionValue
= 0.0;
266 _UpdateDownloadProgress(NULL
, packageName
, completionValue
* 100.0);
270 static const char* progressChars
[] = {
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;
291 int ipart
= (int)(completionValue
* width
);
292 int fpart
= (int)(((completionValue
* width
) - ipart
) * 8);
294 fputs("\r", stdout
); // return to the beginning of the line
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
304 // This part is partially downloaded
305 fputs(progressChars
[fpart
], stdout
);
309 // Also print the progress percentage
310 printf(" %3d%%", (int)(completionValue
* 100));
319 UpdateManager::ProgressPackageDownloadComplete(const char* packageName
)
321 if (fCurrentStep
== ACTION_STEP_DOWNLOAD
) {
322 _UpdateDownloadProgress(NULL
, packageName
, 100.0);
323 fPackageDownloadsCount
++;
327 // Overwrite the progress bar with whitespace
330 ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &w
);
331 for (int i
= 0; i
< (w
.ws_col
); i
++)
333 fputs("\r\x1b[1A", stdout
); // Go to previous line.
335 printf("Downloading %s...done.\n", packageName
);
341 UpdateManager::ProgressPackageChecksumStarted(const char* title
)
343 // Repository checksums
344 if (fCurrentStep
== ACTION_STEP_START
)
345 _UpdateStatusWindow(NULL
, title
);
348 printf("%s...", title
);
353 UpdateManager::ProgressPackageChecksumComplete(const char* title
)
361 UpdateManager::ProgressStartApplyingChanges(InstalledRepository
& repository
)
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());
369 printf("[%s] Applying changes ...\n", repository
.Name().String());
374 UpdateManager::ProgressTransactionCommitted(InstalledRepository
& repository
,
375 const BCommitTransactionResult
& result
)
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 "
381 _FinalUpdate(header
.String(), detail
.String());
384 const char* repositoryName
= repository
.Name().String();
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());
393 printf("[%s] warning: package %s: %s\n", repositoryName
,
394 issue
->PackageName().String(), issue
->ToString().String());
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
);
406 UpdateManager::ProgressApplyingChangesDone(InstalledRepository
& repository
)
409 printf("[%s] Done.\n", repository
.Name().String());
414 UpdateManager::_PrintResult(InstalledRepository
& installationRepository
,
415 int32
& upgradeCount
, int32
& installCount
, int32
& uninstallCount
)
417 if (!installationRepository
.HasChanges())
421 printf(" in %s:\n", installationRepository
.Name().String());
423 PackageList
& packagesToActivate
424 = installationRepository
.PackagesToActivate();
425 PackageList
& packagesToDeactivate
426 = installationRepository
.PackagesToDeactivate();
428 BStringList upgradedPackages
;
429 BStringList upgradedPackageVersions
;
431 BSolverPackage
* installPackage
= packagesToActivate
.ItemAt(i
);
434 BSolverPackage
* uninstallPackage
= packagesToDeactivate
.ItemAt(j
);
436 if (installPackage
->Info().Name() == uninstallPackage
->Info().Name()) {
437 upgradedPackages
.Add(installPackage
->Info().Name());
438 upgradedPackageVersions
.Add(uninstallPackage
->Info().Version().ToString());
444 for (int32 i
= 0; BSolverPackage
* package
= packagesToActivate
.ItemAt(i
);
447 if (dynamic_cast<MiscLocalRepository
*>(package
->Repository()) != NULL
)
448 repository
= "local file";
450 repository
.SetToFormat("repository %s",
451 package
->Repository()->Name().String());
453 int position
= upgradedPackages
.IndexOf(package
->Info().Name());
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());
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(),
478 package
->Info().Version().ToString().String(),
479 package
->Info().Summary().String(),
480 package
->Repository()->Name().String(),
481 package
->Info().FileName().String());
486 BStringList uninstallList
;
487 for (int32 i
= 0; BSolverPackage
* package
= packagesToDeactivate
.ItemAt(i
);
489 if (upgradedPackages
.HasString(package
->Info().Name()))
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(),
498 package
->Info().Summary().String(),
499 package
->Repository()->Name().String(),
500 package
->Info().FileName().String());
507 UpdateManager::_UpdateStatusWindow(const char* header
, const char* detail
)
509 if (header
== NULL
&& detail
== NULL
)
512 if (fStatusWindow
->UserCancelRequested())
513 throw BAbortedByUserException();
515 BMessage
message(kMsgTextUpdate
);
517 message
.AddString(kKeyHeader
, header
);
519 message
.AddString(kKeyDetail
, detail
);
520 fStatusWindow
->PostMessage(&message
);
525 UpdateManager::_UpdateDownloadProgress(const char* header
,
526 const char* packageName
, float percentageComplete
)
528 if (packageName
== NULL
)
531 if (fStatusWindow
->UserCancelRequested())
532 throw BAbortedByUserException();
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
);
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
);
550 UpdateManager::_FinalUpdate(const char* header
, const char* text
)
552 if (!fStatusWindow
->IsFront()) {
553 BNotification
notification(B_INFORMATION_NOTIFICATION
);
554 notification
.SetGroup("SoftwareUpdater");
555 notification
.SetTitle(header
);
556 notification
.SetContent(text
);
560 fStatusWindow
->FinalUpdate(header
, text
);
565 UpdateManager::_SetCurrentStep(int32 step
)