/*
* Copyright 2013-2019, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler <axeld@pinc-software.de>
* Rene Gollent <rene@gollent.com>
* Ingo Weinhold <ingo_weinhold@gmx.de>
* Brian Hill <supernova@tycho.email>
* Jacob Secunda
*/
#include "UpdateManager.h"
#include <sys/ioctl.h>
#include <unistd.h>
#include <Alert.h>
#include <Application.h>
#include <Catalog.h>
#include <Message.h>
#include <Messenger.h>
#include <NetworkInterface.h>
#include <NetworkRoster.h>
#include <Notification.h>
#include <Roster.h>
#include <package/manager/Exceptions.h>
#include <package/solver/SolverPackage.h>
#include "constants.h"
#include "ProblemWindow.h"
using namespace BPackageKit;
using namespace BPackageKit::BManager::BPrivate;
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "UpdateManager"
UpdateManager::UpdateManager(BPackageInstallationLocation location,
bool verbose)
:
BPackageManager(location, &fClientInstallationInterface, this),
BPackageManager::UserInteractionHandler(),
fClientInstallationInterface(),
fStatusWindow(NULL),
fCurrentStep(ACTION_STEP_INIT),
fChangesConfirmed(false),
fVerbose(verbose)
{
fStatusWindow = new SoftwareUpdaterWindow();
_SetCurrentStep(ACTION_STEP_START);
}
UpdateManager::~UpdateManager()
{
if (fStatusWindow != NULL) {
fStatusWindow->Lock();
fStatusWindow->Quit();
}
if (fProblemWindow != NULL) {
fProblemWindow->Lock();
fProblemWindow->Quit();
}
}
void
UpdateManager::CheckNetworkConnection()
{
BNetworkRoster& roster = BNetworkRoster::Default();
BNetworkInterface interface;
uint32 cookie = 0;
while (roster.GetNextInterface(&cookie, interface) == B_OK) {
uint32 flags = interface.Flags();
if ((flags & IFF_LOOPBACK) == 0
&& (flags & (IFF_UP | IFF_LINK)) == (IFF_UP | IFF_LINK)) {
return;
}
}
// No network connection detected, cannot continue
throw BException(B_TRANSLATE_COMMENT(
"No active network connection was found", "Error message"));
}
update_type
UpdateManager::GetUpdateType()
{
int32 action = USER_SELECTION_NEEDED;
BMessenger messenger(fStatusWindow);
if (messenger.IsValid()) {
BMessage message(kMsgGetUpdateType);
BMessage reply;
messenger.SendMessage(&message, &reply);
reply.FindInt32(kKeyAlertResult, &action);
}
return (update_type)action;
}
void
UpdateManager::CheckRepositories()
{
int32 count = fOtherRepositories.CountItems();
if (fVerbose)
printf("Remote repositories available: %" B_PRId32 "\n", count);
if (count == 0) {
BMessenger messenger(fStatusWindow);
if (messenger.IsValid()) {
BMessage message(kMsgNoRepositories);
BMessage reply;
messenger.SendMessage(&message, &reply);
int32 result;
reply.FindInt32(kKeyAlertResult, &result);
if (result == 1)
be_roster->Launch("application/x-vnd.Haiku-Repositories");
}
be_app->PostMessage(kMsgFinalQuit);
throw BException(B_TRANSLATE_COMMENT(
"No remote repositories are available", "Error message"));
}
}
void
UpdateManager::JobFailed(BSupportKit::BJob* job)
{
if (!fVerbose)
return;
BString error = job->ErrorString();
if (error.Length() > 0) {
error.ReplaceAll("\n", "\n*** ");
fprintf(stderr, "%s", error.String());
}
}
void
UpdateManager::JobAborted(BSupportKit::BJob* job)
{
if (fVerbose)
puts("Job aborted");
}
void
UpdateManager::FinalUpdate(const char* header, const char* text)
{
_FinalUpdate(header, text);
}
void
UpdateManager::HandleProblems()
{
if (fProblemWindow == NULL)
fProblemWindow = new ProblemWindow;
ProblemWindow::SolverPackageSet installPackages;
ProblemWindow::SolverPackageSet uninstallPackages;
if (!fProblemWindow->Go(fSolver,installPackages, uninstallPackages))
throw BAbortedByUserException();
fProblemWindow->Hide();
}
void
UpdateManager::ConfirmChanges(bool fromMostSpecific)
{
if (fVerbose)
puts("The following changes will be made:");
int32 count = fInstalledRepositories.CountItems();
int32 upgradeCount = 0;
int32 installCount = 0;
int32 uninstallCount = 0;
if (fromMostSpecific) {
for (int32 i = count - 1; i >= 0; i--)
_PrintResult(*fInstalledRepositories.ItemAt(i), upgradeCount,
installCount, uninstallCount);
} else {
for (int32 i = 0; i < count; i++)
_PrintResult(*fInstalledRepositories.ItemAt(i), upgradeCount,
installCount, uninstallCount);
}
if (fVerbose)
printf("Upgrade count=%" B_PRId32 ", Install count=%" B_PRId32
", Uninstall count=%" B_PRId32 "\n",
upgradeCount, installCount, uninstallCount);
fChangesConfirmed = fStatusWindow->ConfirmUpdates();
if (!fChangesConfirmed)
throw BAbortedByUserException();
_SetCurrentStep(ACTION_STEP_DOWNLOAD);
fPackageDownloadsTotal = upgradeCount + installCount;
fPackageDownloadsCount = 1;
}
void
UpdateManager::Warn(status_t error, const char* format, ...)
{
char buffer[256];
va_list args;
va_start(args, format);
vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
if (fVerbose) {
fputs(buffer, stderr);
if (error == B_OK)
puts("");
else
printf(": %s\n", strerror(error));
}
if (fStatusWindow != NULL) {
if (fStatusWindow->UserCancelRequested())
throw BAbortedByUserException();
fStatusWindow->ShowWarningAlert(buffer);
} else {
BString text("SoftwareUpdater:\n");
text.Append(buffer);
BAlert* alert = new BAlert("warning", text, B_TRANSLATE("OK"), NULL,
NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->Go(NULL);
}
}
void
UpdateManager::ProgressPackageDownloadStarted(const char* packageName)
{
if (fCurrentStep == ACTION_STEP_DOWNLOAD) {
BString header(B_TRANSLATE("Downloading packages"));
_UpdateDownloadProgress(header.String(), packageName, 0.0);
fNewDownloadStarted = false;
}
if (fVerbose)
printf("Downloading %s...\n", packageName);
}
void
UpdateManager::ProgressPackageDownloadActive(const char* packageName,
float completionValue, off_t bytes, off_t totalBytes)
{
if (fCurrentStep == ACTION_STEP_DOWNLOAD) {
// Fix a bug where a 100% completion percentage gets sent at the start
// of a package download
if (!fNewDownloadStarted) {
if (completionValue > 0 && completionValue < 1)
fNewDownloadStarted = true;
else
completionValue = 0.0;
}
_UpdateDownloadProgress(NULL, packageName, completionValue * 100.0);
}
if (fVerbose) {
static const char* progressChars[] = {
"\xE2\x96\x8F",
"\xE2\x96\x8E",
"\xE2\x96\x8D",
"\xE2\x96\x8C",
"\xE2\x96\x8B",
"\xE2\x96\x8A",
"\xE2\x96\x89",
"\xE2\x96\x88",
};
int width = 70;
struct winsize winSize;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winSize) == 0
&& winSize.ws_col < 77) {
// We need 7 characters for the percent display
width = winSize.ws_col - 7;
}
int position;
int ipart = (int)(completionValue * width);
int fpart = (int)(((completionValue * width) - ipart) * 8);
fputs("\r", stdout); // return to the beginning of the line
for (position = 0; position < width; position++) {
if (position < ipart) {
// This part is fully downloaded, show a full block
fputs(progressChars[7], stdout);
} else if (position > ipart) {
// This part is not downloaded, show a space
fputs(" ", stdout);
} else {
// This part is partially downloaded
fputs(progressChars[fpart], stdout);
}
}
// Also print the progress percentage
printf(" %3d%%", (int)(completionValue * 100));
fflush(stdout);
}
}
void
UpdateManager::ProgressPackageDownloadComplete(const char* packageName)
{
if (fCurrentStep == ACTION_STEP_DOWNLOAD) {
_UpdateDownloadProgress(NULL, packageName, 100.0);
fPackageDownloadsCount++;
}
if (fVerbose) {
// Overwrite the progress bar with whitespace
fputs("\r", stdout);
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
for (int i = 0; i < (w.ws_col); i++)
fputs(" ", stdout);
fputs("\r\x1b[1A", stdout); // Go to previous line.
printf("Downloading %s...done.\n", packageName);
}
}
void
UpdateManager::ProgressPackageChecksumStarted(const char* title)
{
// Repository checksums
if (fCurrentStep == ACTION_STEP_START)
_UpdateStatusWindow(NULL, title);
if (fVerbose)
printf("%s...", title);
}
void
UpdateManager::ProgressPackageChecksumComplete(const char* title)
{
if (fVerbose)
puts("done.");
}
void
UpdateManager::ProgressStartApplyingChanges(InstalledRepository& repository)
{
_SetCurrentStep(ACTION_STEP_APPLY);
BString header(B_TRANSLATE("Applying changes"));
BString detail(B_TRANSLATE("Packages are being updated"));
fStatusWindow->UpdatesApplying(header.String(), detail.String());
if (fVerbose)
printf("[%s] Applying changes ...\n", repository.Name().String());
}
void
UpdateManager::ProgressTransactionCommitted(InstalledRepository& repository,
const BCommitTransactionResult& result)
{
_SetCurrentStep(ACTION_STEP_COMPLETE);
BString header(B_TRANSLATE("Updates completed"));
BString detail;
if (BPackageRoster().IsRebootNeeded()) {
detail = B_TRANSLATE("A reboot is necessary to complete the "
"update process.");
fStatusWindow->PostMessage(kMsgShowReboot);
} else {
detail = B_TRANSLATE("Updates have been successfully installed.");
}
_FinalUpdate(header.String(), detail.String());
if (fVerbose) {
const char* repositoryName = repository.Name().String();
int32 issueCount = result.CountIssues();
for (int32 i = 0; i < issueCount; i++) {
const BTransactionIssue* issue = result.IssueAt(i);
if (issue->PackageName().IsEmpty()) {
printf("[%s] warning: %s\n", repositoryName,
issue->ToString().String());
} else {
printf("[%s] warning: package %s: %s\n", repositoryName,
issue->PackageName().String(), issue->ToString().String());
}
}
printf("[%s] Changes applied. Old activation state backed up in \"%s\"\n",
repositoryName, result.OldStateDirectory().String());
printf("[%s] Cleaning up ...\n", repositoryName);
}
}
void
UpdateManager::ProgressApplyingChangesDone(InstalledRepository& repository)
{
if (fVerbose)
printf("[%s] Done.\n", repository.Name().String());
}
void
UpdateManager::_PrintResult(InstalledRepository& installationRepository,
int32& upgradeCount, int32& installCount, int32& uninstallCount)
{
if (!installationRepository.HasChanges())
return;
if (fVerbose)
printf(" in %s:\n", installationRepository.Name().String());
PackageList& packagesToActivate
= installationRepository.PackagesToActivate();
PackageList& packagesToDeactivate
= installationRepository.PackagesToDeactivate();
BStringList upgradedPackages;
BStringList upgradedPackageVersions;
for (int32 i = 0;
BSolverPackage* installPackage = packagesToActivate.ItemAt(i);
i++) {
for (int32 j = 0;
BSolverPackage* uninstallPackage = packagesToDeactivate.ItemAt(j);
j++) {
if (installPackage->Info().Name() == uninstallPackage->Info().Name()) {
upgradedPackages.Add(installPackage->Info().Name());
upgradedPackageVersions.Add(uninstallPackage->Info().Version().ToString());
break;
}
}
}
for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i);
i++) {
BString repository;
if (dynamic_cast<MiscLocalRepository*>(package->Repository()) != NULL)
repository = "local file";
else
repository.SetToFormat("repository %s",
package->Repository()->Name().String());
int position = upgradedPackages.IndexOf(package->Info().Name());
if (position >= 0) {
if (fVerbose)
printf(" upgrade package %s-%s to %s from %s\n",
package->Info().Name().String(),
upgradedPackageVersions.StringAt(position).String(),
package->Info().Version().ToString().String(),
repository.String());
fStatusWindow->AddPackageInfo(PACKAGE_UPDATE,
package->Info().Name().String(),
upgradedPackageVersions.StringAt(position).String(),
package->Info().Version().ToString().String(),
package->Info().Summary().String(),
package->Repository()->Name().String(),
package->Info().FileName().String());
upgradeCount++;
} else {
if (fVerbose)
printf(" install package %s-%s from %s\n",
package->Info().Name().String(),
package->Info().Version().ToString().String(),
repository.String());
fStatusWindow->AddPackageInfo(PACKAGE_INSTALL,
package->Info().Name().String(),
NULL,
package->Info().Version().ToString().String(),
package->Info().Summary().String(),
package->Repository()->Name().String(),
package->Info().FileName().String());
installCount++;
}
}
BStringList uninstallList;
for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i);
i++) {
if (upgradedPackages.HasString(package->Info().Name()))
continue;
if (fVerbose)
printf(" uninstall package %s\n",
package->VersionedName().String());
fStatusWindow->AddPackageInfo(PACKAGE_UNINSTALL,
package->Info().Name().String(),
package->Info().Version().ToString(),
NULL,
package->Info().Summary().String(),
package->Repository()->Name().String(),
package->Info().FileName().String());
uninstallCount++;
}
}
void
UpdateManager::_UpdateStatusWindow(const char* header, const char* detail)
{
if (header == NULL && detail == NULL)
return;
if (fStatusWindow->UserCancelRequested())
throw BAbortedByUserException();
BMessage message(kMsgTextUpdate);
if (header != NULL)
message.AddString(kKeyHeader, header);
if (detail != NULL)
message.AddString(kKeyDetail, detail);
fStatusWindow->PostMessage(&message);
}
void
UpdateManager::_UpdateDownloadProgress(const char* header,
const char* packageName, float percentageComplete)
{
if (packageName == NULL)
return;
if (fStatusWindow->UserCancelRequested())
throw BAbortedByUserException();
BString packageCount;
packageCount.SetToFormat(
B_TRANSLATE_COMMENT("%i of %i", "Do not translate %i"),
fPackageDownloadsCount,
fPackageDownloadsTotal);
BMessage message(kMsgProgressUpdate);
if (header != NULL)
message.AddString(kKeyHeader, header);
message.AddString(kKeyPackageName, packageName);
message.AddString(kKeyPackageCount, packageCount.String());
message.AddFloat(kKeyPercentage, percentageComplete);
fStatusWindow->PostMessage(&message);
}
void
UpdateManager::_FinalUpdate(const char* header, const char* text)
{
if (!fStatusWindow->IsFront()) {
BNotification notification(B_INFORMATION_NOTIFICATION);
notification.SetGroup("SoftwareUpdater");
notification.SetTitle(header);
notification.SetContent(text);
notification.Send();
}
fStatusWindow->FinalUpdate(header, text);
}
void
UpdateManager::_SetCurrentStep(int32 step)
{
fCurrentStep = step;
}
↑ V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.