xref: /haiku/src/bin/pkgman/PackageManager.cpp (revision 0de25abadc86e260328c6f7c4255acbee8f70d4e)
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 	fInteractive(interactive)
34 {
35 }
36 
37 
38 PackageManager::~PackageManager()
39 {
40 }
41 
42 
43 void
44 PackageManager::SetInteractive(bool interactive)
45 {
46 	fInteractive = interactive;
47 	fDecisionProvider.SetInteractive(interactive);
48 }
49 
50 
51 void
52 PackageManager::JobFailed(BJob* job)
53 {
54 	BString error = job->ErrorString();
55 	if (error.Length() > 0) {
56 		error.ReplaceAll("\n", "\n*** ");
57 		fprintf(stderr, "%s", error.String());
58 	}
59 }
60 
61 
62 void
63 PackageManager::JobAborted(BJob* job)
64 {
65 	DIE(job->Result(), "aborted");
66 }
67 
68 
69 void
70 PackageManager::HandleProblems()
71 {
72 	printf("Encountered problems:\n");
73 
74 	int32 problemCount = fSolver->CountProblems();
75 	for (int32 i = 0; i < problemCount; i++) {
76 		// print problem and possible solutions
77 		BSolverProblem* problem = fSolver->ProblemAt(i);
78 		printf("problem %" B_PRId32 ": %s\n", i + 1,
79 			problem->ToString().String());
80 
81 		int32 solutionCount = problem->CountSolutions();
82 		for (int32 k = 0; k < solutionCount; k++) {
83 			const BSolverProblemSolution* solution = problem->SolutionAt(k);
84 			printf("  solution %" B_PRId32 ":\n", k + 1);
85 			int32 elementCount = solution->CountElements();
86 			for (int32 l = 0; l < elementCount; l++) {
87 				const BSolverProblemSolutionElement* element
88 					= solution->ElementAt(l);
89 				printf("    - %s\n", element->ToString().String());
90 			}
91 		}
92 
93 		if (!fInteractive)
94 			continue;
95 
96 		// let the user choose a solution
97 		printf("Please select a solution, skip the problem for now or quit.\n");
98 		for (;;) {
99 			if (solutionCount > 1)
100 				printf("select [1...%" B_PRId32 "/s/q]: ", solutionCount);
101 			else
102 				printf("select [1/s/q]: ");
103 
104 			char buffer[32];
105 			if (fgets(buffer, sizeof(buffer), stdin) == NULL
106 				|| strcmp(buffer, "q\n") == 0) {
107 				exit(1);
108 			}
109 
110 			if (strcmp(buffer, "s\n") == 0)
111 				break;
112 
113 			char* end;
114 			long selected = strtol(buffer, &end, 0);
115 			if (end == buffer || *end != '\n' || selected < 1
116 				|| selected > solutionCount) {
117 				printf("*** invalid input\n");
118 				continue;
119 			}
120 
121 			status_t error = fSolver->SelectProblemSolution(problem,
122 				problem->SolutionAt(selected - 1));
123 			if (error != B_OK)
124 				DIE(error, "failed to set solution");
125 			break;
126 		}
127 	}
128 
129 	if (problemCount > 0 && !fInteractive)
130 		exit(1);
131 }
132 
133 
134 void
135 PackageManager::ConfirmChanges(bool fromMostSpecific)
136 {
137 	printf("The following changes will be made:\n");
138 
139 	int32 count = fInstalledRepositories.CountItems();
140 	if (fromMostSpecific) {
141 		for (int32 i = count - 1; i >= 0; i--)
142 			_PrintResult(*fInstalledRepositories.ItemAt(i));
143 	} else {
144 		for (int32 i = 0; i < count; i++)
145 			_PrintResult(*fInstalledRepositories.ItemAt(i));
146 	}
147 
148 	if (!fDecisionProvider.YesNoDecisionNeeded(BString(), "Continue?", "y", "n",
149 			"y")) {
150 		exit(1);
151 	}
152 }
153 
154 
155 void
156 PackageManager::Warn(status_t error, const char* format, ...)
157 {
158 	va_list args;
159 	va_start(args, format);
160 	vfprintf(stderr, format, args);
161 	va_end(args);
162 
163 	if (error == B_OK)
164 		printf("\n");
165 	else
166 		printf(": %s\n", strerror(error));
167 }
168 
169 
170 void
171 PackageManager::ProgressPackageDownloadStarted(const char* packageName)
172 {
173 	printf("Downloading %s...\n", packageName);
174 }
175 
176 
177 void
178 PackageManager::ProgressPackageDownloadActive(const char* packageName,
179 	float completionPercentage)
180 {
181 	static const char* progressChars[] = {
182 		"\xE2\x96\x8F",
183 		"\xE2\x96\x8E",
184 		"\xE2\x96\x8D",
185 		"\xE2\x96\x8C",
186 		"\xE2\x96\x8B",
187 		"\xE2\x96\x8A",
188 		"\xE2\x96\x89",
189 		"\xE2\x96\x88",
190 	};
191 
192 	const int width = 70;
193 
194 	int position;
195 	int ipart = (int)(completionPercentage * width);
196 	int fpart = (int)(((completionPercentage * width) - ipart) * 8);
197 
198 	printf("\r"); // erase the line
199 
200 	for (position = 0; position < width; position++) {
201 		if (position < ipart) {
202 			// This part is fully downloaded, show a full block
203 			printf(progressChars[7]);
204 		} else if (position > ipart) {
205 			// This part is not downloaded, show a space
206 			printf(" ");
207 		} else {
208 			// This part is partially downloaded
209 			printf(progressChars[fpart]);
210 		}
211 	}
212 
213 	// Also print the progress percentage
214 	printf(" %3d%%", (int)(completionPercentage * 100));
215 
216 	fflush(stdout);
217 }
218 
219 
220 void
221 PackageManager::ProgressPackageDownloadComplete(const char* packageName)
222 {
223 	printf("\nFinished downloading %s.\n", packageName);
224 }
225 
226 
227 void
228 PackageManager::ProgressPackageChecksumStarted(const char* title)
229 {
230 	printf("%s...\n", title);
231 }
232 
233 
234 void
235 PackageManager::ProgressPackageChecksumComplete(const char* title)
236 {
237 	printf("%s complete.\n", title);
238 }
239 
240 
241 void
242 PackageManager::ProgressStartApplyingChanges(InstalledRepository& repository)
243 {
244 	printf("[%s] Applying changes ...\n", repository.Name().String());
245 }
246 
247 
248 void
249 PackageManager::ProgressTransactionCommitted(InstalledRepository& repository,
250 	const BCommitTransactionResult& result)
251 {
252 	const char* repositoryName = repository.Name().String();
253 
254 	int32 issueCount = result.CountIssues();
255 	for (int32 i = 0; i < issueCount; i++) {
256 		const BTransactionIssue* issue = result.IssueAt(i);
257 		if (issue->PackageName().IsEmpty()) {
258 			printf("[%s] warning: %s\n", repositoryName,
259 				issue->ToString().String());
260 		} else {
261 			printf("[%s] warning: package %s: %s\n", repositoryName,
262 				issue->PackageName().String(), issue->ToString().String());
263 		}
264 	}
265 
266 	printf("[%s] Changes applied. Old activation state backed up in \"%s\"\n",
267 		repositoryName, result.OldStateDirectory().String());
268 	printf("[%s] Cleaning up ...\n", repositoryName);
269 }
270 
271 
272 void
273 PackageManager::ProgressApplyingChangesDone(InstalledRepository& repository)
274 {
275 	printf("[%s] Done.\n", repository.Name().String());
276 }
277 
278 
279 void
280 PackageManager::_PrintResult(InstalledRepository& installationRepository)
281 {
282 	if (!installationRepository.HasChanges())
283 		return;
284 
285 	printf("  in %s:\n", installationRepository.Name().String());
286 
287 	PackageList& packagesToActivate
288 		= installationRepository.PackagesToActivate();
289 	PackageList& packagesToDeactivate
290 		= installationRepository.PackagesToDeactivate();
291 
292 	for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i);
293 		i++) {
294 		if (dynamic_cast<MiscLocalRepository*>(package->Repository()) == NULL) {
295 			printf("    install package %s from repository %s\n",
296 				package->Info().FileName().String(),
297 				package->Repository()->Name().String());
298 		} else {
299 			printf("    install package %s from local file\n",
300 				package->Info().FileName().String());
301 		}
302 	}
303 
304 	for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i);
305 		i++) {
306 		printf("    uninstall package %s\n", package->VersionedName().String());
307 	}
308 // TODO: Print file/download sizes. Unfortunately our package infos don't
309 // contain the file size. Which is probably correct. The file size (and possibly
310 // other information) should, however, be provided by the repository cache in
311 // some way. Extend BPackageInfo? Create a BPackageFileInfo?
312 }
313