libroot/posix/stdio: Remove unused portions.
[haiku.git] / src / apps / softwareupdater / UpdateManager.cpp
blobaa9f295344a2a862036dc5797af14aa196052296
1 /*
2 * Copyright 2013-2017, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
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>
13 #include "UpdateManager.h"
15 #include <sys/ioctl.h>
16 #include <unistd.h>
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>
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,
42 bool verbose)
44 BPackageManager(location, &fClientInstallationInterface, this),
45 BPackageManager::UserInteractionHandler(),
46 fClientInstallationInterface(),
47 fStatusWindow(NULL),
48 fCurrentStep(ACTION_STEP_INIT),
49 fChangesConfirmed(false),
50 fVerbose(verbose)
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();
70 void
71 UpdateManager::CheckNetworkConnection()
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;
84 // No network connection detected, cannot continue
85 throw BException(B_TRANSLATE_COMMENT(
86 "No active network connection was found", "Error message"));
90 update_type
91 UpdateManager::GetUpdateType()
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);
101 return (update_type)action;
105 void
106 UpdateManager::CheckRepositories()
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");
122 be_app->PostMessage(kMsgFinalQuit);
123 throw BException(B_TRANSLATE_COMMENT(
124 "No remote repositories are available", "Error message"));
129 void
130 UpdateManager::JobFailed(BSupportKit::BJob* job)
132 if (!fVerbose)
133 return;
135 BString error = job->ErrorString();
136 if (error.Length() > 0) {
137 error.ReplaceAll("\n", "\n*** ");
138 fprintf(stderr, "%s", error.String());
143 void
144 UpdateManager::JobAborted(BSupportKit::BJob* job)
146 if (fVerbose)
147 puts("Job aborted");
151 void
152 UpdateManager::FinalUpdate(const char* header, const char* text)
154 _FinalUpdate(header, text);
158 void
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();
172 void
173 UpdateManager::ConfirmChanges(bool fromMostSpecific)
175 if (fVerbose)
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);
187 } else {
188 for (int32 i = 0; i < count; i++)
189 _PrintResult(*fInstalledRepositories.ItemAt(i), upgradeCount,
190 installCount, uninstallCount);
193 if (fVerbose)
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;
208 void
209 UpdateManager::Warn(status_t error, const char* format, ...)
211 char buffer[256];
212 va_list args;
213 va_start(args, format);
214 vsnprintf(buffer, sizeof(buffer), format, args);
215 va_end(args);
217 if (fVerbose) {
218 fputs(buffer, stderr);
219 if (error == B_OK)
220 puts("");
221 else
222 printf(": %s\n", strerror(error));
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);
239 void
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;
248 if (fVerbose)
249 printf("Downloading %s...\n", packageName);
253 void
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;
263 else
264 completionValue = 0.0;
266 _UpdateDownloadProgress(NULL, packageName, completionValue * 100.0);
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",
281 int width = 70;
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;
290 int position;
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
302 fputs(" ", stdout);
303 } else {
304 // This part is partially downloaded
305 fputs(progressChars[fpart], stdout);
309 // Also print the progress percentage
310 printf(" %3d%%", (int)(completionValue * 100));
312 fflush(stdout);
318 void
319 UpdateManager::ProgressPackageDownloadComplete(const char* packageName)
321 if (fCurrentStep == ACTION_STEP_DOWNLOAD) {
322 _UpdateDownloadProgress(NULL, packageName, 100.0);
323 fPackageDownloadsCount++;
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.
335 printf("Downloading %s...done.\n", packageName);
340 void
341 UpdateManager::ProgressPackageChecksumStarted(const char* title)
343 // Repository checksums
344 if (fCurrentStep == ACTION_STEP_START)
345 _UpdateStatusWindow(NULL, title);
347 if (fVerbose)
348 printf("%s...", title);
352 void
353 UpdateManager::ProgressPackageChecksumComplete(const char* title)
355 if (fVerbose)
356 puts("done.");
360 void
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());
368 if (fVerbose)
369 printf("[%s] Applying changes ...\n", repository.Name().String());
373 void
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 "
380 "updates."));
381 _FinalUpdate(header.String(), detail.String());
383 if (fVerbose) {
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());
392 } else {
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);
405 void
406 UpdateManager::ProgressApplyingChangesDone(InstalledRepository& repository)
408 if (fVerbose)
409 printf("[%s] Done.\n", repository.Name().String());
413 void
414 UpdateManager::_PrintResult(InstalledRepository& installationRepository,
415 int32& upgradeCount, int32& installCount, int32& uninstallCount)
417 if (!installationRepository.HasChanges())
418 return;
420 if (fVerbose)
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;
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;
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());
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++;
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++;
506 void
507 UpdateManager::_UpdateStatusWindow(const char* header, const char* detail)
509 if (header == NULL && detail == NULL)
510 return;
512 if (fStatusWindow->UserCancelRequested())
513 throw BAbortedByUserException();
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);
524 void
525 UpdateManager::_UpdateDownloadProgress(const char* header,
526 const char* packageName, float percentageComplete)
528 if (packageName == NULL)
529 return;
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);
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);
549 void
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);
557 notification.Send();
560 fStatusWindow->FinalUpdate(header, text);
564 void
565 UpdateManager::_SetCurrentStep(int32 step)
567 fCurrentStep = step;