DHCP initial timeout 0.25s from 4s
[haiku.git] / src / apps / softwareupdater / UpdateManager.cpp
blob77d08cba4e16b1ccf464410fadb36605d743eeb6
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@warpmail.net>
13 #include "UpdateManager.h"
15 #include <sys/ioctl.h>
16 #include <unistd.h>
18 #include <Alert.h>
19 #include <Catalog.h>
20 #include <Message.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(),
51 fStatusWindow(NULL),
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);
69 void
70 UpdateManager::CheckNetworkConnection()
72 BNetworkRoster& roster = BNetworkRoster::Default();
73 BNetworkInterface interface;
74 uint32 cookie = 0;
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)) {
79 return;
83 // No network connection detected, display warning
84 int32 result = 0;
85 BMessenger messenger(fStatusWindow);
86 if (messenger.IsValid()) {
87 BMessage message(kMsgNetworkAlert);
88 BMessage reply;
89 messenger.SendMessage(&message, &reply);
90 reply.FindInt32(kKeyAlertResult, &result);
91 } else {
92 BAlert* alert = new BAlert("network_connection",
93 B_TRANSLATE_COMMENT("No active network connection was found",
94 "Alert message"),
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);
98 result = alert->Go();
100 if (result)
101 throw BAbortedByUserException();
105 void
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());
116 void
117 UpdateManager::JobAborted(BSupportKit::BJob* job)
119 printf("Job aborted\n");
123 void
124 UpdateManager::FinalUpdate(const char* header, const char* text)
126 _FinalUpdate(header, text);
130 void
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");
162 for (;;) {
163 if (solutionCount > 1)
164 printf("select [1...%" B_PRId32 "/s/q]: ", solutionCount);
165 else
166 printf("select [1/s/q]: ");
168 char buffer[32];
169 if (fgets(buffer, sizeof(buffer), stdin) == NULL
170 || strcmp(buffer, "q\n") == 0) {
171 //exit(1);
174 if (strcmp(buffer, "s\n") == 0)
175 break;
178 char* end;
179 long selected = strtol(buffer, &end, 0);
180 if (end == buffer || *end != '\n' || selected < 1
181 || selected > solutionCount) {
182 printf("*** invalid input\n");
183 continue;
186 status_t error = fSolver->SelectProblemSolution(problem,
187 problem->SolutionAt(selected - 1));
188 if (error != B_OK)
189 DIE(error, "failed to set solution");
191 break;
197 void
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);
211 } else {
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);
220 BString text;
221 if (upgradeCount == 1)
222 text.SetTo(B_TRANSLATE_COMMENT("There is 1 update "
223 "%dependancies%available.",
224 "Do not translate %dependancies%"));
225 else
226 text.SetTo(B_TRANSLATE_COMMENT("There are %count% updates "
227 "%dependancies%available.",
228 "Do not translate %count% or %dependancies%"));
229 BString countString;
230 countString << upgradeCount;
231 text.ReplaceFirst("%count%", countString);
232 BString dependancies("");
233 if (installCount) {
234 dependancies.SetTo("(");
235 dependancies.Append(B_TRANSLATE("with")).Append(" ");
236 if (installCount == 1)
237 dependancies.Append(B_TRANSLATE("1 new dependancy"));
238 else {
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;
255 void
256 UpdateManager::Warn(status_t error, const char* format, ...)
258 char buffer[256];
259 va_list args;
260 va_start(args, format);
261 vfprintf(stderr, format, args);
262 vsnprintf(buffer, sizeof(buffer), format, args);
263 va_end(args);
265 if (error == B_OK)
266 printf("\n");
267 else
268 printf(": %s\n", strerror(error));
270 if (fStatusWindow != NULL) {
271 if (fStatusWindow->UserCancelRequested())
272 throw BAbortedByUserException();
273 fStatusWindow->ShowWarningAlert(buffer);
274 } else {
275 BString text("SoftwareUpdater:\n");
276 text.Append(buffer);
277 BAlert* alert = new BAlert("warning", text, B_TRANSLATE("OK"), NULL,
278 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
279 alert->Go(NULL);
284 void
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,
295 0.0);
296 fNewDownloadStarted = false;
299 printf("Downloading %s...\n", packageName);
303 void
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;
313 else
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[] = {
326 "\xE2\x96\x8F",
327 "\xE2\x96\x8E",
328 "\xE2\x96\x8D",
329 "\xE2\x96\x8C",
330 "\xE2\x96\x8B",
331 "\xE2\x96\x8A",
332 "\xE2\x96\x89",
333 "\xE2\x96\x88",
336 int width = 70;
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;
345 int position;
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
357 printf(" ");
358 } else {
359 // This part is partially downloaded
360 printf(progressChars[fpart]);
364 // Also print the progress percentage
365 printf(" %3d%%", (int)(completionPercentage * 100));
367 fflush(stdout);
372 void
373 UpdateManager::ProgressPackageDownloadComplete(const char* packageName)
375 if (fCurrentStep == ACTION_STEP_DOWNLOAD)
376 fPackageDownloadsCount++;
378 // Overwrite the progress bar with whitespace
379 printf("\r");
380 struct winsize w;
381 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
382 for (int i = 0; i < (w.ws_col); i++)
383 printf(" ");
384 printf("\r\x1b[1A"); // Go to previous line.
386 printf("Downloading %s...done.\n", packageName);
390 void
391 UpdateManager::ProgressPackageChecksumStarted(const char* title)
393 // Repository checksums
394 if (fCurrentStep == ACTION_STEP_START)
395 _UpdateStatusWindow(NULL, title);
397 printf("%s...", title);
401 void
402 UpdateManager::ProgressPackageChecksumComplete(const char* title)
404 printf("done.\n");
408 void
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());
420 void
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 "
427 "updates."));
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());
438 } else {
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);
450 void
451 UpdateManager::ProgressApplyingChangesDone(InstalledRepository& repository)
453 printf("[%s] Done.\n", repository.Name().String());
457 void
458 UpdateManager::_PrintResult(InstalledRepository& installationRepository,
459 int32& upgradeCount, int32& installCount, int32& uninstallCount)
461 if (!installationRepository.HasChanges())
462 return;
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;
473 for (int32 i = 0;
474 BSolverPackage* installPackage = packagesToActivate.ItemAt(i);
475 i++) {
476 for (int32 j = 0;
477 BSolverPackage* uninstallPackage = packagesToDeactivate.ItemAt(j);
478 j++) {
479 if (installPackage->Info().Name() == uninstallPackage->Info().Name()) {
480 upgradedPackages.Add(installPackage->Info().Name());
481 upgradedPackageVersions.Add(uninstallPackage->Info().Version().ToString());
482 break;
487 for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i);
488 i++) {
489 BString repository;
490 if (dynamic_cast<MiscLocalRepository*>(package->Repository()) != NULL)
491 repository = "local file";
492 else
493 repository.SetToFormat("repository %s",
494 package->Repository()->Name().String());
496 int position = upgradedPackages.IndexOf(package->Info().Name());
497 if (position >= 0) {
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());
509 upgradeCount++;
510 } else {
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(),
517 NULL,
518 package->Info().Version().ToString().String(),
519 package->Info().Summary().String(),
520 package->Repository()->Name().String());
521 installCount++;
525 BStringList uninstallList;
526 for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i);
527 i++) {
528 if (upgradedPackages.HasString(package->Info().Name()))
529 continue;
530 printf(" uninstall package %s\n", package->VersionedName().String());
531 fStatusWindow->AddPackageInfo(PACKAGE_UNINSTALL,
532 package->Info().Name().String(),
533 package->Info().Version().ToString(),
534 NULL,
535 package->Info().Summary().String(),
536 package->Repository()->Name().String());
537 uninstallCount++;
542 void
543 UpdateManager::_UpdateStatusWindow(const char* header, const char* detail)
545 if (header == NULL && detail == NULL)
546 return;
548 if (fStatusWindow->UserCancelRequested())
549 throw BAbortedByUserException();
551 BMessage message(kMsgTextUpdate);
552 if (header != NULL)
553 message.AddString(kKeyHeader, header);
554 if (detail != NULL)
555 message.AddString(kKeyDetail, detail);
556 fStatusWindow->PostMessage(&message);
560 void
561 UpdateManager::_UpdateDownloadProgress(const char* header,
562 const char* packageName, const char* packageCount,
563 float completionPercentage)
565 if (packageName == NULL || packageCount == NULL)
566 return;
568 if (fStatusWindow->UserCancelRequested())
569 throw BAbortedByUserException();
571 BMessage message(kMsgProgressUpdate);
572 if (header != NULL)
573 message.AddString(kKeyHeader, header);
574 message.AddString(kKeyPackageName, packageName);
575 message.AddString(kKeyPackageCount, packageCount);
576 message.AddFloat(kKeyPercentage, completionPercentage);
577 fStatusWindow->PostMessage(&message);
581 void
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());
589 if (icon.IsValid())
590 notification.SetIcon(&icon);
591 notification.Send();
593 fStatusWindow->FinalUpdate(header, text);
597 void
598 UpdateManager::_SetCurrentStep(int32 step)
600 fCurrentStep = step;