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@warpmail.net>
13 #include "UpdateManager.h"
15 #include <sys/ioctl.h>
21 #include <Messenger.h>
22 #include <NetworkInterface.h>
23 #include <NetworkRoster.h>
24 #include <Notification.h>
26 #include <package/CommitTransactionResult.h>
27 #include <package/DownloadFileRequest.h>
28 #include <package/RefreshRepositoryRequest.h>
29 #include <package/manager/Exceptions.h>
30 #include <package/solver/SolverPackage.h>
31 #include <package/solver/SolverProblem.h>
32 #include <package/solver/SolverProblemSolution.h>
34 #include "constants.h"
35 #include "AutoDeleter.h"
36 #include "ProblemWindow.h"
37 #include "ResultWindow.h"
39 using namespace BPackageKit
;
40 using namespace BPackageKit::BManager::BPrivate
;
42 #undef B_TRANSLATION_CONTEXT
43 #define B_TRANSLATION_CONTEXT "UpdateManager"
46 UpdateManager::UpdateManager(BPackageInstallationLocation location
)
48 BPackageManager(location
, &fClientInstallationInterface
, this),
49 BPackageManager::UserInteractionHandler(),
50 fClientInstallationInterface(),
52 fCurrentStep(ACTION_STEP_INIT
),
53 fChangesConfirmed(false)
55 fStatusWindow
= new SoftwareUpdaterWindow();
56 _SetCurrentStep(ACTION_STEP_START
);
60 UpdateManager::~UpdateManager()
62 if (fStatusWindow
!= NULL
)
63 fStatusWindow
->PostMessage(B_QUIT_REQUESTED
);
64 if (fProblemWindow
!= NULL
)
65 fProblemWindow
->PostMessage(B_QUIT_REQUESTED
);
70 UpdateManager::CheckNetworkConnection()
72 BNetworkRoster
& roster
= BNetworkRoster::Default();
73 BNetworkInterface interface
;
75 while (roster
.GetNextInterface(&cookie
, interface
) == B_OK
) {
76 uint32 flags
= interface
.Flags();
77 if ((flags
& IFF_LOOPBACK
) == 0
78 && (flags
& (IFF_UP
| IFF_LINK
)) == (IFF_UP
| IFF_LINK
)) {
83 // No network connection detected, display warning
85 BMessenger
messenger(fStatusWindow
);
86 if (messenger
.IsValid()) {
87 BMessage
message(kMsgNetworkAlert
);
89 messenger
.SendMessage(&message
, &reply
);
90 reply
.FindInt32(kKeyAlertResult
, &result
);
92 BAlert
* alert
= new BAlert("network_connection",
93 B_TRANSLATE_COMMENT("No active network connection was found",
95 B_TRANSLATE_COMMENT("Continue anyway", "Alert button label"),
96 B_TRANSLATE_COMMENT("Quit","Alert button label"),
97 NULL
, B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
101 throw BAbortedByUserException();
106 UpdateManager::JobFailed(BSupportKit::BJob
* job
)
108 BString error
= job
->ErrorString();
109 if (error
.Length() > 0) {
110 error
.ReplaceAll("\n", "\n*** ");
111 fprintf(stderr
, "%s", error
.String());
117 UpdateManager::JobAborted(BSupportKit::BJob
* job
)
119 printf("Job aborted\n");
124 UpdateManager::FinalUpdate(const char* header
, const char* text
)
126 _FinalUpdate(header
, text
);
131 UpdateManager::HandleProblems()
133 if (fProblemWindow
== NULL
)
134 fProblemWindow
= new ProblemWindow
;
136 ProblemWindow::SolverPackageSet installPackages
;
137 ProblemWindow::SolverPackageSet uninstallPackages
;
138 if (!fProblemWindow
->Go(fSolver
,installPackages
, uninstallPackages
))
139 throw BAbortedByUserException();
141 /* int32 problemCount = fSolver->CountProblems();
142 for (int32 i = 0; i < problemCount; i++) {
143 // print problem and possible solutions
144 BSolverProblem* problem = fSolver->ProblemAt(i);
145 printf("problem %" B_PRId32 ": %s\n", i + 1,
146 problem->ToString().String());
148 int32 solutionCount = problem->CountSolutions();
149 for (int32 k = 0; k < solutionCount; k++) {
150 const BSolverProblemSolution* solution = problem->SolutionAt(k);
151 printf(" solution %" B_PRId32 ":\n", k + 1);
152 int32 elementCount = solution->CountElements();
153 for (int32 l = 0; l < elementCount; l++) {
154 const BSolverProblemSolutionElement* element
155 = solution->ElementAt(l);
156 printf(" - %s\n", element->ToString().String());
160 // let the user choose a solution
161 printf("Please select a solution, skip the problem for now or quit.\n");
163 if (solutionCount > 1)
164 printf("select [1...%" B_PRId32 "/s/q]: ", solutionCount);
166 printf("select [1/s/q]: ");
169 if (fgets(buffer, sizeof(buffer), stdin) == NULL
170 || strcmp(buffer, "q\n") == 0) {
174 if (strcmp(buffer, "s\n") == 0)
179 long selected = strtol(buffer, &end, 0);
180 if (end == buffer || *end != '\n' || selected < 1
181 || selected > solutionCount) {
182 printf("*** invalid input\n");
186 status_t error = fSolver->SelectProblemSolution(problem,
187 problem->SolutionAt(selected - 1));
189 DIE(error, "failed to set solution");
198 UpdateManager::ConfirmChanges(bool fromMostSpecific
)
200 printf("The following changes will be made:\n");
202 int32 count
= fInstalledRepositories
.CountItems();
203 int32 upgradeCount
= 0;
204 int32 installCount
= 0;
205 int32 uninstallCount
= 0;
207 if (fromMostSpecific
) {
208 for (int32 i
= count
- 1; i
>= 0; i
--)
209 _PrintResult(*fInstalledRepositories
.ItemAt(i
), upgradeCount
,
210 installCount
, uninstallCount
);
212 for (int32 i
= 0; i
< count
; i
++)
213 _PrintResult(*fInstalledRepositories
.ItemAt(i
), upgradeCount
,
214 installCount
, uninstallCount
);
217 printf("Upgrade count=%" B_PRId32
", Install count=%" B_PRId32
218 ", Uninstall count=%" B_PRId32
"\n",
219 upgradeCount
, installCount
, uninstallCount
);
221 if (upgradeCount
== 1)
222 text
.SetTo(B_TRANSLATE_COMMENT("There is 1 update "
223 "%dependancies%available.",
224 "Do not translate %dependancies%"));
226 text
.SetTo(B_TRANSLATE_COMMENT("There are %count% updates "
227 "%dependancies%available.",
228 "Do not translate %count% or %dependancies%"));
230 countString
<< upgradeCount
;
231 text
.ReplaceFirst("%count%", countString
);
232 BString
dependancies("");
234 dependancies
.SetTo("(");
235 dependancies
.Append(B_TRANSLATE("with")).Append(" ");
236 if (installCount
== 1)
237 dependancies
.Append(B_TRANSLATE("1 new dependancy"));
239 dependancies
<< installCount
;
240 dependancies
.Append(" ").Append(B_TRANSLATE("new dependancies"));
242 dependancies
.Append(") ");
244 text
.ReplaceFirst("%dependancies%", dependancies
);
245 fChangesConfirmed
= fStatusWindow
->ConfirmUpdates(text
.String());
246 if (!fChangesConfirmed
)
247 throw BAbortedByUserException();
249 _SetCurrentStep(ACTION_STEP_DOWNLOAD
);
250 fPackageDownloadsTotal
= upgradeCount
+ installCount
;
251 fPackageDownloadsCount
= 1;
256 UpdateManager::Warn(status_t error
, const char* format
, ...)
260 va_start(args
, format
);
261 vfprintf(stderr
, format
, args
);
262 vsnprintf(buffer
, sizeof(buffer
), format
, args
);
268 printf(": %s\n", strerror(error
));
270 if (fStatusWindow
!= NULL
) {
271 if (fStatusWindow
->UserCancelRequested())
272 throw BAbortedByUserException();
273 fStatusWindow
->ShowWarningAlert(buffer
);
275 BString
text("SoftwareUpdater:\n");
277 BAlert
* alert
= new BAlert("warning", text
, B_TRANSLATE("OK"), NULL
,
278 NULL
, B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
285 UpdateManager::ProgressPackageDownloadStarted(const char* packageName
)
287 if (fCurrentStep
== ACTION_STEP_DOWNLOAD
) {
288 BString
header(B_TRANSLATE("Downloading packages"));
289 BString packageCount
;
290 packageCount
.SetToFormat(
291 B_TRANSLATE_COMMENT("%i of %i", "Do not translate %i"),
292 fPackageDownloadsCount
,
293 fPackageDownloadsTotal
);
294 _UpdateDownloadProgress(header
.String(), packageName
, packageCount
,
296 fNewDownloadStarted
= false;
299 printf("Downloading %s...\n", packageName
);
304 UpdateManager::ProgressPackageDownloadActive(const char* packageName
,
305 float completionPercentage
, off_t bytes
, off_t totalBytes
)
307 if (fCurrentStep
== ACTION_STEP_DOWNLOAD
) {
308 // Fix a bug where a 100% completion percentage gets sent at the start
309 // of a package download
310 if (!fNewDownloadStarted
) {
311 if (completionPercentage
> 0 && completionPercentage
< 1)
312 fNewDownloadStarted
= true;
314 completionPercentage
= 0.0;
316 BString packageCount
;
317 packageCount
.SetToFormat(
318 B_TRANSLATE_COMMENT("%i of %i", "Do not translate %i"),
319 fPackageDownloadsCount
,
320 fPackageDownloadsTotal
);
321 _UpdateDownloadProgress(NULL
, packageName
, packageCount
,
322 completionPercentage
);
325 static const char* progressChars
[] = {
338 struct winsize winSize
;
339 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &winSize
) == 0
340 && winSize
.ws_col
< 77) {
341 // We need 7 characters for the percent display
342 width
= winSize
.ws_col
- 7;
346 int ipart
= (int)(completionPercentage
* width
);
347 int fpart
= (int)(((completionPercentage
* width
) - ipart
) * 8);
349 printf("\r"); // return to the beginning of the line
351 for (position
= 0; position
< width
; position
++) {
352 if (position
< ipart
) {
353 // This part is fully downloaded, show a full block
354 printf(progressChars
[7]);
355 } else if (position
> ipart
) {
356 // This part is not downloaded, show a space
359 // This part is partially downloaded
360 printf(progressChars
[fpart
]);
364 // Also print the progress percentage
365 printf(" %3d%%", (int)(completionPercentage
* 100));
373 UpdateManager::ProgressPackageDownloadComplete(const char* packageName
)
375 if (fCurrentStep
== ACTION_STEP_DOWNLOAD
)
376 fPackageDownloadsCount
++;
378 // Overwrite the progress bar with whitespace
381 ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &w
);
382 for (int i
= 0; i
< (w
.ws_col
); i
++)
384 printf("\r\x1b[1A"); // Go to previous line.
386 printf("Downloading %s...done.\n", packageName
);
391 UpdateManager::ProgressPackageChecksumStarted(const char* title
)
393 // Repository checksums
394 if (fCurrentStep
== ACTION_STEP_START
)
395 _UpdateStatusWindow(NULL
, title
);
397 printf("%s...", title
);
402 UpdateManager::ProgressPackageChecksumComplete(const char* title
)
409 UpdateManager::ProgressStartApplyingChanges(InstalledRepository
& repository
)
411 _SetCurrentStep(ACTION_STEP_APPLY
);
412 BString
header(B_TRANSLATE("Applying changes"));
413 BString
detail(B_TRANSLATE("Packages are being updated"));
414 fStatusWindow
->UpdatesApplying(header
.String(), detail
.String());
416 printf("[%s] Applying changes ...\n", repository
.Name().String());
421 UpdateManager::ProgressTransactionCommitted(InstalledRepository
& repository
,
422 const BCommitTransactionResult
& result
)
424 _SetCurrentStep(ACTION_STEP_COMPLETE
);
425 BString
header(B_TRANSLATE("Updates completed"));
426 BString
detail(B_TRANSLATE("A reboot may be necessary to complete some "
428 _FinalUpdate(header
.String(), detail
.String());
430 const char* repositoryName
= repository
.Name().String();
432 int32 issueCount
= result
.CountIssues();
433 for (int32 i
= 0; i
< issueCount
; i
++) {
434 const BTransactionIssue
* issue
= result
.IssueAt(i
);
435 if (issue
->PackageName().IsEmpty()) {
436 printf("[%s] warning: %s\n", repositoryName
,
437 issue
->ToString().String());
439 printf("[%s] warning: package %s: %s\n", repositoryName
,
440 issue
->PackageName().String(), issue
->ToString().String());
444 printf("[%s] Changes applied. Old activation state backed up in \"%s\"\n",
445 repositoryName
, result
.OldStateDirectory().String());
446 printf("[%s] Cleaning up ...\n", repositoryName
);
451 UpdateManager::ProgressApplyingChangesDone(InstalledRepository
& repository
)
453 printf("[%s] Done.\n", repository
.Name().String());
458 UpdateManager::_PrintResult(InstalledRepository
& installationRepository
,
459 int32
& upgradeCount
, int32
& installCount
, int32
& uninstallCount
)
461 if (!installationRepository
.HasChanges())
464 printf(" in %s:\n", installationRepository
.Name().String());
466 PackageList
& packagesToActivate
467 = installationRepository
.PackagesToActivate();
468 PackageList
& packagesToDeactivate
469 = installationRepository
.PackagesToDeactivate();
471 BStringList upgradedPackages
;
472 BStringList upgradedPackageVersions
;
474 BSolverPackage
* installPackage
= packagesToActivate
.ItemAt(i
);
477 BSolverPackage
* uninstallPackage
= packagesToDeactivate
.ItemAt(j
);
479 if (installPackage
->Info().Name() == uninstallPackage
->Info().Name()) {
480 upgradedPackages
.Add(installPackage
->Info().Name());
481 upgradedPackageVersions
.Add(uninstallPackage
->Info().Version().ToString());
487 for (int32 i
= 0; BSolverPackage
* package
= packagesToActivate
.ItemAt(i
);
490 if (dynamic_cast<MiscLocalRepository
*>(package
->Repository()) != NULL
)
491 repository
= "local file";
493 repository
.SetToFormat("repository %s",
494 package
->Repository()->Name().String());
496 int position
= upgradedPackages
.IndexOf(package
->Info().Name());
498 printf(" upgrade package %s-%s to %s from %s\n",
499 package
->Info().Name().String(),
500 upgradedPackageVersions
.StringAt(position
).String(),
501 package
->Info().Version().ToString().String(),
502 repository
.String());
503 fStatusWindow
->AddPackageInfo(PACKAGE_UPDATE
,
504 package
->Info().Name().String(),
505 upgradedPackageVersions
.StringAt(position
).String(),
506 package
->Info().Version().ToString().String(),
507 package
->Info().Summary().String(),
508 package
->Repository()->Name().String());
511 printf(" install package %s-%s from %s\n",
512 package
->Info().Name().String(),
513 package
->Info().Version().ToString().String(),
514 repository
.String());
515 fStatusWindow
->AddPackageInfo(PACKAGE_INSTALL
,
516 package
->Info().Name().String(),
518 package
->Info().Version().ToString().String(),
519 package
->Info().Summary().String(),
520 package
->Repository()->Name().String());
525 BStringList uninstallList
;
526 for (int32 i
= 0; BSolverPackage
* package
= packagesToDeactivate
.ItemAt(i
);
528 if (upgradedPackages
.HasString(package
->Info().Name()))
530 printf(" uninstall package %s\n", package
->VersionedName().String());
531 fStatusWindow
->AddPackageInfo(PACKAGE_UNINSTALL
,
532 package
->Info().Name().String(),
533 package
->Info().Version().ToString(),
535 package
->Info().Summary().String(),
536 package
->Repository()->Name().String());
543 UpdateManager::_UpdateStatusWindow(const char* header
, const char* detail
)
545 if (header
== NULL
&& detail
== NULL
)
548 if (fStatusWindow
->UserCancelRequested())
549 throw BAbortedByUserException();
551 BMessage
message(kMsgTextUpdate
);
553 message
.AddString(kKeyHeader
, header
);
555 message
.AddString(kKeyDetail
, detail
);
556 fStatusWindow
->PostMessage(&message
);
561 UpdateManager::_UpdateDownloadProgress(const char* header
,
562 const char* packageName
, const char* packageCount
,
563 float completionPercentage
)
565 if (packageName
== NULL
|| packageCount
== NULL
)
568 if (fStatusWindow
->UserCancelRequested())
569 throw BAbortedByUserException();
571 BMessage
message(kMsgProgressUpdate
);
573 message
.AddString(kKeyHeader
, header
);
574 message
.AddString(kKeyPackageName
, packageName
);
575 message
.AddString(kKeyPackageCount
, packageCount
);
576 message
.AddFloat(kKeyPercentage
, completionPercentage
);
577 fStatusWindow
->PostMessage(&message
);
582 UpdateManager::_FinalUpdate(const char* header
, const char* text
)
584 BNotification
notification(B_INFORMATION_NOTIFICATION
);
585 notification
.SetGroup("SoftwareUpdater");
586 notification
.SetTitle(header
);
587 notification
.SetContent(text
);
588 BBitmap
icon(fStatusWindow
->GetNotificationIcon());
590 notification
.SetIcon(&icon
);
593 fStatusWindow
->FinalUpdate(header
, text
);
598 UpdateManager::_SetCurrentStep(int32 step
)