xref: /haiku/src/bin/pkgman/PackageManager.cpp (revision d06cbe081b7ea043aea2012359744091de6d604d)
1 /*
2  * Copyright 2013-2014, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Ingo Weinhold <ingo_weinhold@gmx.de>
7  *		Rene Gollent <rene@gollent.com>
8  */
9 
10 
11 #include "PackageManager.h"
12 
13 #include <package/CommitTransactionResult.h>
14 #include <package/DownloadFileRequest.h>
15 #include <package/RefreshRepositoryRequest.h>
16 #include <package/solver/SolverPackage.h>
17 #include <package/solver/SolverProblem.h>
18 #include <package/solver/SolverProblemSolution.h>
19 
20 #include "pkgman.h"
21 
22 
23 using namespace BPackageKit::BPrivate;
24 
25 
26 PackageManager::PackageManager(BPackageInstallationLocation location,
27 	bool interactive)
28 	:
29 	BPackageManager(location, &fClientInstallationInterface, this),
30 	BPackageManager::UserInteractionHandler(),
31 	fDecisionProvider(interactive),
32 	fClientInstallationInterface(),
33 	fPreviousDownloadPercentage(0),
34 	fInteractive(interactive)
35 {
36 }
37 
38 
39 PackageManager::~PackageManager()
40 {
41 }
42 
43 
44 void
45 PackageManager::SetInteractive(bool interactive)
46 {
47 	fInteractive = interactive;
48 	fDecisionProvider.SetInteractive(interactive);
49 }
50 
51 
52 void
53 PackageManager::JobFailed(BJob* job)
54 {
55 	BString error = job->ErrorString();
56 	if (error.Length() > 0) {
57 		error.ReplaceAll("\n", "\n*** ");
58 		fprintf(stderr, "%s", error.String());
59 	}
60 }
61 
62 
63 void
64 PackageManager::JobAborted(BJob* job)
65 {
66 	DIE(job->Result(), "aborted");
67 }
68 
69 
70 void
71 PackageManager::HandleProblems()
72 {
73 	printf("Encountered problems:\n");
74 
75 	int32 problemCount = fSolver->CountProblems();
76 	for (int32 i = 0; i < problemCount; i++) {
77 		// print problem and possible solutions
78 		BSolverProblem* problem = fSolver->ProblemAt(i);
79 		printf("problem %" B_PRId32 ": %s\n", i + 1,
80 			problem->ToString().String());
81 
82 		int32 solutionCount = problem->CountSolutions();
83 		for (int32 k = 0; k < solutionCount; k++) {
84 			const BSolverProblemSolution* solution = problem->SolutionAt(k);
85 			printf("  solution %" B_PRId32 ":\n", k + 1);
86 			int32 elementCount = solution->CountElements();
87 			for (int32 l = 0; l < elementCount; l++) {
88 				const BSolverProblemSolutionElement* element
89 					= solution->ElementAt(l);
90 				printf("    - %s\n", element->ToString().String());
91 			}
92 		}
93 
94 		if (!fInteractive)
95 			continue;
96 
97 		// let the user choose a solution
98 		printf("Please select a solution, skip the problem for now or quit.\n");
99 		for (;;) {
100 			if (solutionCount > 1)
101 				printf("select [1...%" B_PRId32 "/s/q]: ", solutionCount);
102 			else
103 				printf("select [1/s/q]: ");
104 
105 			char buffer[32];
106 			if (fgets(buffer, sizeof(buffer), stdin) == NULL
107 				|| strcmp(buffer, "q\n") == 0) {
108 				exit(1);
109 			}
110 
111 			if (strcmp(buffer, "s\n") == 0)
112 				break;
113 
114 			char* end;
115 			long selected = strtol(buffer, &end, 0);
116 			if (end == buffer || *end != '\n' || selected < 1
117 				|| selected > solutionCount) {
118 				printf("*** invalid input\n");
119 				continue;
120 			}
121 
122 			status_t error = fSolver->SelectProblemSolution(problem,
123 				problem->SolutionAt(selected - 1));
124 			if (error != B_OK)
125 				DIE(error, "failed to set solution");
126 			break;
127 		}
128 	}
129 
130 	if (problemCount > 0 && !fInteractive)
131 		exit(1);
132 }
133 
134 
135 void
136 PackageManager::ConfirmChanges(bool fromMostSpecific)
137 {
138 	printf("The following changes will be made:\n");
139 
140 	int32 count = fInstalledRepositories.CountItems();
141 	if (fromMostSpecific) {
142 		for (int32 i = count - 1; i >= 0; i--)
143 			_PrintResult(*fInstalledRepositories.ItemAt(i));
144 	} else {
145 		for (int32 i = 0; i < count; i++)
146 			_PrintResult(*fInstalledRepositories.ItemAt(i));
147 	}
148 
149 	if (!fDecisionProvider.YesNoDecisionNeeded(BString(), "Continue?", "y", "n",
150 			"y")) {
151 		exit(1);
152 	}
153 }
154 
155 
156 void
157 PackageManager::Warn(status_t error, const char* format, ...)
158 {
159 	va_list args;
160 	va_start(args, format);
161 	vfprintf(stderr, format, args);
162 	va_end(args);
163 
164 	if (error == B_OK)
165 		printf("\n");
166 	else
167 		printf(": %s\n", strerror(error));
168 }
169 
170 
171 void
172 PackageManager::ProgressPackageDownloadStarted(const char* packageName)
173 {
174 	printf("Downloading %s...\n", packageName);
175 	fPreviousDownloadPercentage = 0;
176 }
177 
178 
179 void
180 PackageManager::ProgressPackageDownloadActive(const char* packageName,
181 	float completionPercentage)
182 {
183 	int32 currentPercentage = int32(completionPercentage * 100);
184 	int32 difference = currentPercentage - fPreviousDownloadPercentage;
185 
186 	while (difference >= 2) {
187 		printf("#");
188 		difference -= 2;
189 	}
190 	fflush(stdout);
191 
192 	fPreviousDownloadPercentage = currentPercentage - difference;
193 }
194 
195 
196 void
197 PackageManager::ProgressPackageDownloadComplete(const char* packageName)
198 {
199 	printf("\nFinished downloading %s.\n", packageName);
200 }
201 
202 
203 void
204 PackageManager::ProgressPackageChecksumStarted(const char* title)
205 {
206 	printf("%s...\n", title);
207 }
208 
209 
210 void
211 PackageManager::ProgressPackageChecksumComplete(const char* title)
212 {
213 	printf("%s complete.\n", title);
214 }
215 
216 
217 void
218 PackageManager::ProgressStartApplyingChanges(InstalledRepository& repository)
219 {
220 	printf("[%s] Applying changes ...\n", repository.Name().String());
221 }
222 
223 
224 void
225 PackageManager::ProgressTransactionCommitted(InstalledRepository& repository,
226 	const BCommitTransactionResult& result)
227 {
228 	const char* repositoryName = repository.Name().String();
229 
230 	int32 issueCount = result.CountIssues();
231 	for (int32 i = 0; i < issueCount; i++) {
232 		const BTransactionIssue* issue = result.IssueAt(i);
233 		if (issue->PackageName().IsEmpty()) {
234 			printf("[%s] warning: %s\n", repositoryName,
235 				issue->ToString().String());
236 		} else {
237 			printf("[%s] warning: package %s: %s\n", repositoryName,
238 				issue->PackageName().String(), issue->ToString().String());
239 		}
240 	}
241 
242 	printf("[%s] Changes applied. Old activation state backed up in \"%s\"\n",
243 		repositoryName, result.OldStateDirectory().String());
244 	printf("[%s] Cleaning up ...\n", repositoryName);
245 }
246 
247 
248 void
249 PackageManager::ProgressApplyingChangesDone(InstalledRepository& repository)
250 {
251 	printf("[%s] Done.\n", repository.Name().String());
252 }
253 
254 
255 void
256 PackageManager::_PrintResult(InstalledRepository& installationRepository)
257 {
258 	if (!installationRepository.HasChanges())
259 		return;
260 
261 	printf("  in %s:\n", installationRepository.Name().String());
262 
263 	PackageList& packagesToActivate
264 		= installationRepository.PackagesToActivate();
265 	PackageList& packagesToDeactivate
266 		= installationRepository.PackagesToDeactivate();
267 
268 	for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i);
269 		i++) {
270 		if (dynamic_cast<MiscLocalRepository*>(package->Repository()) == NULL) {
271 			printf("    install package %s from repository %s\n",
272 				package->Info().FileName().String(),
273 				package->Repository()->Name().String());
274 		} else {
275 			printf("    install package %s from local file\n",
276 				package->Info().FileName().String());
277 		}
278 	}
279 
280 	for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i);
281 		i++) {
282 		printf("    uninstall package %s\n", package->VersionedName().String());
283 	}
284 // TODO: Print file/download sizes. Unfortunately our package infos don't
285 // contain the file size. Which is probably correct. The file size (and possibly
286 // other information) should, however, be provided by the repository cache in
287 // some way. Extend BPackageInfo? Create a BPackageFileInfo?
288 }
289