vfs: check userland buffers before reading them.
[haiku.git] / src / bin / pkgman / PackageManager.cpp
bloba93e301d409875e13b7a62972ead2797fe948716
1 /*
2 * Copyright 2013-2015, 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 */
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>
21 #include <unistd.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>
30 #include "pkgman.h"
33 using namespace BPackageKit::BPrivate;
36 PackageManager::PackageManager(BPackageInstallationLocation location,
37 bool interactive)
39 BPackageManager(location, &fClientInstallationInterface, this),
40 BPackageManager::UserInteractionHandler(),
41 fDecisionProvider(interactive),
42 fClientInstallationInterface(),
43 fInteractive(interactive)
48 PackageManager::~PackageManager()
53 void
54 PackageManager::SetInteractive(bool interactive)
56 fInteractive = interactive;
57 fDecisionProvider.SetInteractive(interactive);
61 void
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());
72 void
73 PackageManager::JobAborted(BSupportKit::BJob* job)
75 DIE(job->Result(), "aborted");
79 void
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());
103 if (!fInteractive)
104 continue;
106 // let the user choose a solution
107 printf("Please select a solution, skip the problem for now or quit.\n");
108 for (;;) {
109 if (solutionCount > 1)
110 printf("select [1...%" B_PRId32 "/s/q]: ", solutionCount);
111 else
112 printf("select [1/s/q]: ");
114 char buffer[32];
115 if (fgets(buffer, sizeof(buffer), stdin) == NULL
116 || strcmp(buffer, "q\n") == 0) {
117 exit(1);
120 if (strcmp(buffer, "s\n") == 0)
121 break;
123 char* end;
124 long selected = strtol(buffer, &end, 0);
125 if (end == buffer || *end != '\n' || selected < 1
126 || selected > solutionCount) {
127 printf("*** invalid input\n");
128 continue;
131 status_t error = fSolver->SelectProblemSolution(problem,
132 problem->SolutionAt(selected - 1));
133 if (error != B_OK)
134 DIE(error, "failed to set solution");
135 break;
139 if (problemCount > 0 && !fInteractive)
140 exit(1);
144 void
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));
153 } else {
154 for (int32 i = 0; i < count; i++)
155 _PrintResult(*fInstalledRepositories.ItemAt(i));
158 if (!fDecisionProvider.YesNoDecisionNeeded(BString(), "Continue?", "yes",
159 "no", "yes")) {
160 exit(1);
165 void
166 PackageManager::Warn(status_t error, const char* format, ...)
168 va_list args;
169 va_start(args, format);
170 vfprintf(stderr, format, args);
171 va_end(args);
173 if (error == B_OK)
174 printf("\n");
175 else
176 printf(": %s\n", strerror(error));
180 void
181 PackageManager::ProgressPackageDownloadStarted(const char* packageName)
183 fLastBytes = 0;
184 fLastRateCalcTime = system_time();
185 fDownloadRate = 0;
186 printf(" 0%%");
190 void
191 PackageManager::ProgressPackageDownloadActive(const char* packageName,
192 float completionPercentage, off_t bytes, off_t totalBytes)
194 if (bytes == totalBytes)
195 fLastBytes = totalBytes;
196 if (!fInteractive)
197 return;
199 // Do not update if nothing changed in the last 500ms
200 if (bytes <= fLastBytes || (system_time() - fLastRateCalcTime) < 500000)
201 return;
203 const bigtime_t time = system_time();
204 if (time != fLastRateCalcTime) {
205 fDownloadRate = (bytes - fLastBytes) * 1000000
206 / (time - fLastRateCalcTime);
208 fLastRateCalcTime = time;
209 fLastBytes = bytes;
211 // Build the current file progress percentage and size string
212 BString leftStr;
213 BString rightStr;
215 int width = 70;
216 struct winsize winSize;
217 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winSize) == 0)
218 width = std::min(winSize.ws_col - 2, 78);
220 if (width < 30) {
221 // Not much space for anything, just draw a percentage
222 leftStr.SetToFormat("%3d%%", (int)(completionPercentage * 100));
223 } else {
224 leftStr.SetToFormat("%3d%% %s", (int)(completionPercentage * 100),
225 packageName);
227 char byteBuffer[32];
228 char totalBuffer[32];
229 char rateBuffer[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
261 fflush(stdout);
265 void
266 PackageManager::ProgressPackageDownloadComplete(const char* packageName)
268 if (fInteractive) {
269 // Erase the line, return to the start, and reset colors
270 printf("\r\33[2K\r\x1B[0m");
273 char byteBuffer[32];
274 printf("100%% %s [%s]\n", packageName,
275 string_for_size(fLastBytes, byteBuffer, sizeof(byteBuffer)));
276 fflush(stdout);
280 void
281 PackageManager::ProgressPackageChecksumStarted(const char* title)
283 printf("%s...", title);
287 void
288 PackageManager::ProgressPackageChecksumComplete(const char* title)
290 printf("done.\n");
294 void
295 PackageManager::ProgressStartApplyingChanges(InstalledRepository& repository)
297 printf("[%s] Applying changes ...\n", repository.Name().String());
301 void
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());
313 } else {
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);
325 void
326 PackageManager::ProgressApplyingChangesDone(InstalledRepository& repository)
328 printf("[%s] Done.\n", repository.Name().String());
332 void
333 PackageManager::_PrintResult(InstalledRepository& installationRepository)
335 if (!installationRepository.HasChanges())
336 return;
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;
347 for (int32 i = 0;
348 BSolverPackage* installPackage = packagesToActivate.ItemAt(i);
349 i++) {
350 for (int32 j = 0;
351 BSolverPackage* uninstallPackage = packagesToDeactivate.ItemAt(j);
352 j++) {
353 if (installPackage->Info().Name() == uninstallPackage->Info().Name()) {
354 upgradedPackages.Add(installPackage->Info().Name());
355 upgradedPackageVersions.Add(uninstallPackage->Info().Version().ToString());
356 break;
361 for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i);
362 i++) {
363 BString repository;
364 if (dynamic_cast<MiscLocalRepository*>(package->Repository()) != NULL)
365 repository = "local file";
366 else
367 repository.SetToFormat("repository %s", package->Repository()->Name().String());
369 int position = upgradedPackages.IndexOf(package->Info().Name());
370 if (position >= 0) {
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());
376 } else {
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);
385 i++) {
386 if (upgradedPackages.HasString(package->Info().Name()))
387 continue;
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?