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