2 * Copyright 2013-2015, 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>
12 #include <StringForSize.h>
13 #include <StringForRate.h>
14 // Must be first, or the BPrivate namespaces are confused
16 #include "PackageManager.h"
18 #include <InterfaceDefs.h>
20 #include <sys/ioctl.h>
23 #include <package/CommitTransactionResult.h>
24 #include <package/DownloadFileRequest.h>
25 #include <package/RefreshRepositoryRequest.h>
26 #include <package/solver/SolverPackage.h>
27 #include <package/solver/SolverProblem.h>
28 #include <package/solver/SolverProblemSolution.h>
33 using namespace BPackageKit::BPrivate
;
36 PackageManager::PackageManager(BPackageInstallationLocation location
,
39 BPackageManager(location
, &fClientInstallationInterface
, this),
40 BPackageManager::UserInteractionHandler(),
41 fDecisionProvider(interactive
),
42 fClientInstallationInterface(),
43 fInteractive(interactive
)
48 PackageManager::~PackageManager()
54 PackageManager::SetInteractive(bool interactive
)
56 fInteractive
= interactive
;
57 fDecisionProvider
.SetInteractive(interactive
);
62 PackageManager::JobFailed(BSupportKit::BJob
* job
)
64 BString error
= job
->ErrorString();
65 if (error
.Length() > 0) {
66 error
.ReplaceAll("\n", "\n*** ");
67 fprintf(stderr
, "%s", error
.String());
73 PackageManager::JobAborted(BSupportKit::BJob
* job
)
75 DIE(job
->Result(), "aborted");
80 PackageManager::HandleProblems()
82 printf("Encountered problems:\n");
84 int32 problemCount
= fSolver
->CountProblems();
85 for (int32 i
= 0; i
< problemCount
; i
++) {
86 // print problem and possible solutions
87 BSolverProblem
* problem
= fSolver
->ProblemAt(i
);
88 printf("problem %" B_PRId32
": %s\n", i
+ 1,
89 problem
->ToString().String());
91 int32 solutionCount
= problem
->CountSolutions();
92 for (int32 k
= 0; k
< solutionCount
; k
++) {
93 const BSolverProblemSolution
* solution
= problem
->SolutionAt(k
);
94 printf(" solution %" B_PRId32
":\n", k
+ 1);
95 int32 elementCount
= solution
->CountElements();
96 for (int32 l
= 0; l
< elementCount
; l
++) {
97 const BSolverProblemSolutionElement
* element
98 = solution
->ElementAt(l
);
99 printf(" - %s\n", element
->ToString().String());
106 // let the user choose a solution
107 printf("Please select a solution, skip the problem for now or quit.\n");
109 if (solutionCount
> 1)
110 printf("select [1...%" B_PRId32
"/s/q]: ", solutionCount
);
112 printf("select [1/s/q]: ");
115 if (fgets(buffer
, sizeof(buffer
), stdin
) == NULL
116 || strcmp(buffer
, "q\n") == 0) {
120 if (strcmp(buffer
, "s\n") == 0)
124 long selected
= strtol(buffer
, &end
, 0);
125 if (end
== buffer
|| *end
!= '\n' || selected
< 1
126 || selected
> solutionCount
) {
127 printf("*** invalid input\n");
131 status_t error
= fSolver
->SelectProblemSolution(problem
,
132 problem
->SolutionAt(selected
- 1));
134 DIE(error
, "failed to set solution");
139 if (problemCount
> 0 && !fInteractive
)
145 PackageManager::ConfirmChanges(bool fromMostSpecific
)
147 printf("The following changes will be made:\n");
149 int32 count
= fInstalledRepositories
.CountItems();
150 if (fromMostSpecific
) {
151 for (int32 i
= count
- 1; i
>= 0; i
--)
152 _PrintResult(*fInstalledRepositories
.ItemAt(i
));
154 for (int32 i
= 0; i
< count
; i
++)
155 _PrintResult(*fInstalledRepositories
.ItemAt(i
));
158 if (!fDecisionProvider
.YesNoDecisionNeeded(BString(), "Continue?", "yes",
166 PackageManager::Warn(status_t error
, const char* format
, ...)
169 va_start(args
, format
);
170 vfprintf(stderr
, format
, args
);
176 printf(": %s\n", strerror(error
));
181 PackageManager::ProgressPackageDownloadStarted(const char* packageName
)
184 fLastRateCalcTime
= system_time();
191 PackageManager::ProgressPackageDownloadActive(const char* packageName
,
192 float completionPercentage
, off_t bytes
, off_t totalBytes
)
194 if (bytes
== totalBytes
)
195 fLastBytes
= totalBytes
;
199 // Do not update if nothing changed in the last 500ms
200 if (bytes
<= fLastBytes
|| (system_time() - fLastRateCalcTime
) < 500000)
203 const bigtime_t time
= system_time();
204 if (time
!= fLastRateCalcTime
) {
205 fDownloadRate
= (bytes
- fLastBytes
) * 1000000
206 / (time
- fLastRateCalcTime
);
208 fLastRateCalcTime
= time
;
211 // Build the current file progress percentage and size string
216 struct winsize winSize
;
217 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &winSize
) == 0)
218 width
= std::min(winSize
.ws_col
- 2, 78);
221 // Not much space for anything, just draw a percentage
222 leftStr
.SetToFormat("%3d%%", (int)(completionPercentage
* 100));
224 leftStr
.SetToFormat("%3d%% %s", (int)(completionPercentage
* 100),
228 char totalBuffer
[32];
230 rightStr
.SetToFormat("%s/%s %s ",
231 string_for_size(bytes
, byteBuffer
, sizeof(byteBuffer
)),
232 string_for_size(totalBytes
, totalBuffer
, sizeof(totalBuffer
)),
233 fDownloadRate
== 0 ? "--.-" :
234 string_for_rate(fDownloadRate
, rateBuffer
, sizeof(rateBuffer
)));
236 if (leftStr
.CountChars() + rightStr
.CountChars() >= width
)
238 // The full string does not fit! Try to make a shorter one.
239 leftStr
.ReplaceLast(".hpkg", "");
240 leftStr
.TruncateChars(width
- rightStr
.CountChars() - 2);
241 leftStr
.Append(B_UTF8_ELLIPSIS
" ");
244 int extraSpace
= width
- leftStr
.CountChars() - rightStr
.CountChars();
246 leftStr
.Append(' ', extraSpace
);
247 leftStr
.Append(rightStr
);
250 const int progChars
= leftStr
.CountBytes(0,
251 (int)(width
* completionPercentage
));
253 // Set bg to green, fg to white, and print progress bar.
254 // Then reset colors and print rest of text
255 // And finally remove any stray chars at the end of the line
256 printf("\r\x1B[42;37m%.*s\x1B[0m%s\x1B[K", progChars
, leftStr
.String(),
257 leftStr
.String() + progChars
);
259 // Force terminal to update when the line is complete, to avoid flickering
260 // because of updates at random times
266 PackageManager::ProgressPackageDownloadComplete(const char* packageName
)
269 // Erase the line, return to the start, and reset colors
270 printf("\r\33[2K\r\x1B[0m");
274 printf("100%% %s [%s]\n", packageName
,
275 string_for_size(fLastBytes
, byteBuffer
, sizeof(byteBuffer
)));
281 PackageManager::ProgressPackageChecksumStarted(const char* title
)
283 printf("%s...", title
);
288 PackageManager::ProgressPackageChecksumComplete(const char* title
)
295 PackageManager::ProgressStartApplyingChanges(InstalledRepository
& repository
)
297 printf("[%s] Applying changes ...\n", repository
.Name().String());
302 PackageManager::ProgressTransactionCommitted(InstalledRepository
& repository
,
303 const BCommitTransactionResult
& result
)
305 const char* repositoryName
= repository
.Name().String();
307 int32 issueCount
= result
.CountIssues();
308 for (int32 i
= 0; i
< issueCount
; i
++) {
309 const BTransactionIssue
* issue
= result
.IssueAt(i
);
310 if (issue
->PackageName().IsEmpty()) {
311 printf("[%s] warning: %s\n", repositoryName
,
312 issue
->ToString().String());
314 printf("[%s] warning: package %s: %s\n", repositoryName
,
315 issue
->PackageName().String(), issue
->ToString().String());
319 printf("[%s] Changes applied. Old activation state backed up in \"%s\"\n",
320 repositoryName
, result
.OldStateDirectory().String());
321 printf("[%s] Cleaning up ...\n", repositoryName
);
326 PackageManager::ProgressApplyingChangesDone(InstalledRepository
& repository
)
328 printf("[%s] Done.\n", repository
.Name().String());
333 PackageManager::_PrintResult(InstalledRepository
& installationRepository
)
335 if (!installationRepository
.HasChanges())
338 printf(" in %s:\n", installationRepository
.Name().String());
340 PackageList
& packagesToActivate
341 = installationRepository
.PackagesToActivate();
342 PackageList
& packagesToDeactivate
343 = installationRepository
.PackagesToDeactivate();
345 BStringList upgradedPackages
;
346 BStringList upgradedPackageVersions
;
348 BSolverPackage
* installPackage
= packagesToActivate
.ItemAt(i
);
351 BSolverPackage
* uninstallPackage
= packagesToDeactivate
.ItemAt(j
);
353 if (installPackage
->Info().Name() == uninstallPackage
->Info().Name()) {
354 upgradedPackages
.Add(installPackage
->Info().Name());
355 upgradedPackageVersions
.Add(uninstallPackage
->Info().Version().ToString());
361 for (int32 i
= 0; BSolverPackage
* package
= packagesToActivate
.ItemAt(i
);
364 if (dynamic_cast<MiscLocalRepository
*>(package
->Repository()) != NULL
)
365 repository
= "local file";
367 repository
.SetToFormat("repository %s", package
->Repository()->Name().String());
369 int position
= upgradedPackages
.IndexOf(package
->Info().Name());
371 printf(" upgrade package %s-%s to %s from %s\n",
372 package
->Info().Name().String(),
373 upgradedPackageVersions
.StringAt(position
).String(),
374 package
->Info().Version().ToString().String(),
375 repository
.String());
377 printf(" install package %s-%s from %s\n",
378 package
->Info().Name().String(),
379 package
->Info().Version().ToString().String(),
380 repository
.String());
384 for (int32 i
= 0; BSolverPackage
* package
= packagesToDeactivate
.ItemAt(i
);
386 if (upgradedPackages
.HasString(package
->Info().Name()))
388 printf(" uninstall package %s\n", package
->VersionedName().String());
390 // TODO: Print file/download sizes. Unfortunately our package infos don't
391 // contain the file size. Which is probably correct. The file size (and possibly
392 // other information) should, however, be provided by the repository cache in
393 // some way. Extend BPackageInfo? Create a BPackageFileInfo?