1 /* 2 * Copyright 2013-2015, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler <axeld@pinc-software.de> 7 * Rene Gollent <rene@gollent.com> 8 * Ingo Weinhold <ingo_weinhold@gmx.de> 9 */ 10 11 12 #include "PackageManager.h" 13 14 #include <sys/ioctl.h> 15 #include <unistd.h> 16 17 #include <package/CommitTransactionResult.h> 18 #include <package/DownloadFileRequest.h> 19 #include <package/RefreshRepositoryRequest.h> 20 #include <package/solver/SolverPackage.h> 21 #include <package/solver/SolverProblem.h> 22 #include <package/solver/SolverProblemSolution.h> 23 24 #include "pkgman.h" 25 26 27 using namespace BPackageKit::BPrivate; 28 29 30 PackageManager::PackageManager(BPackageInstallationLocation location, 31 bool interactive) 32 : 33 BPackageManager(location, &fClientInstallationInterface, this), 34 BPackageManager::UserInteractionHandler(), 35 fDecisionProvider(interactive), 36 fClientInstallationInterface(), 37 fInteractive(interactive) 38 { 39 } 40 41 42 PackageManager::~PackageManager() 43 { 44 } 45 46 47 void 48 PackageManager::SetInteractive(bool interactive) 49 { 50 fInteractive = interactive; 51 fDecisionProvider.SetInteractive(interactive); 52 } 53 54 55 void 56 PackageManager::JobFailed(BSupportKit::BJob* job) 57 { 58 BString error = job->ErrorString(); 59 if (error.Length() > 0) { 60 error.ReplaceAll("\n", "\n*** "); 61 fprintf(stderr, "%s", error.String()); 62 } 63 } 64 65 66 void 67 PackageManager::JobAborted(BSupportKit::BJob* job) 68 { 69 DIE(job->Result(), "aborted"); 70 } 71 72 73 void 74 PackageManager::HandleProblems() 75 { 76 printf("Encountered problems:\n"); 77 78 int32 problemCount = fSolver->CountProblems(); 79 for (int32 i = 0; i < problemCount; i++) { 80 // print problem and possible solutions 81 BSolverProblem* problem = fSolver->ProblemAt(i); 82 printf("problem %" B_PRId32 ": %s\n", i + 1, 83 problem->ToString().String()); 84 85 int32 solutionCount = problem->CountSolutions(); 86 for (int32 k = 0; k < solutionCount; k++) { 87 const BSolverProblemSolution* solution = problem->SolutionAt(k); 88 printf(" solution %" B_PRId32 ":\n", k + 1); 89 int32 elementCount = solution->CountElements(); 90 for (int32 l = 0; l < elementCount; l++) { 91 const BSolverProblemSolutionElement* element 92 = solution->ElementAt(l); 93 printf(" - %s\n", element->ToString().String()); 94 } 95 } 96 97 if (!fInteractive) 98 continue; 99 100 // let the user choose a solution 101 printf("Please select a solution, skip the problem for now or quit.\n"); 102 for (;;) { 103 if (solutionCount > 1) 104 printf("select [1...%" B_PRId32 "/s/q]: ", solutionCount); 105 else 106 printf("select [1/s/q]: "); 107 108 char buffer[32]; 109 if (fgets(buffer, sizeof(buffer), stdin) == NULL 110 || strcmp(buffer, "q\n") == 0) { 111 exit(1); 112 } 113 114 if (strcmp(buffer, "s\n") == 0) 115 break; 116 117 char* end; 118 long selected = strtol(buffer, &end, 0); 119 if (end == buffer || *end != '\n' || selected < 1 120 || selected > solutionCount) { 121 printf("*** invalid input\n"); 122 continue; 123 } 124 125 status_t error = fSolver->SelectProblemSolution(problem, 126 problem->SolutionAt(selected - 1)); 127 if (error != B_OK) 128 DIE(error, "failed to set solution"); 129 break; 130 } 131 } 132 133 if (problemCount > 0 && !fInteractive) 134 exit(1); 135 } 136 137 138 void 139 PackageManager::ConfirmChanges(bool fromMostSpecific) 140 { 141 printf("The following changes will be made:\n"); 142 143 int32 count = fInstalledRepositories.CountItems(); 144 if (fromMostSpecific) { 145 for (int32 i = count - 1; i >= 0; i--) 146 _PrintResult(*fInstalledRepositories.ItemAt(i)); 147 } else { 148 for (int32 i = 0; i < count; i++) 149 _PrintResult(*fInstalledRepositories.ItemAt(i)); 150 } 151 152 if (!fDecisionProvider.YesNoDecisionNeeded(BString(), "Continue?", "yes", 153 "no", "yes")) { 154 exit(1); 155 } 156 } 157 158 159 void 160 PackageManager::Warn(status_t error, const char* format, ...) 161 { 162 va_list args; 163 va_start(args, format); 164 vfprintf(stderr, format, args); 165 va_end(args); 166 167 if (error == B_OK) 168 printf("\n"); 169 else 170 printf(": %s\n", strerror(error)); 171 } 172 173 174 void 175 PackageManager::ProgressPackageDownloadStarted(const char* packageName) 176 { 177 printf("Downloading %s...\n", packageName); 178 } 179 180 181 void 182 PackageManager::ProgressPackageDownloadActive(const char* packageName, 183 float completionPercentage, off_t bytes, off_t totalBytes) 184 { 185 if (!fInteractive) 186 return; 187 188 static const char* progressChars[] = { 189 "\xE2\x96\x8F", 190 "\xE2\x96\x8E", 191 "\xE2\x96\x8D", 192 "\xE2\x96\x8C", 193 "\xE2\x96\x8B", 194 "\xE2\x96\x8A", 195 "\xE2\x96\x89", 196 "\xE2\x96\x88", 197 }; 198 199 int width = 70; 200 201 struct winsize winSize; 202 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winSize) == 0 203 && winSize.ws_col < 77) { 204 // We need 7 characters for the percent display 205 width = winSize.ws_col - 7; 206 } 207 208 int position; 209 int ipart = (int)(completionPercentage * width); 210 int fpart = (int)(((completionPercentage * width) - ipart) * 8); 211 212 printf("\r"); // return to the beginning of the line 213 214 for (position = 0; position < width; position++) { 215 if (position < ipart) { 216 // This part is fully downloaded, show a full block 217 printf(progressChars[7]); 218 } else if (position > ipart) { 219 // This part is not downloaded, show a space 220 printf(" "); 221 } else { 222 // This part is partially downloaded 223 printf(progressChars[fpart]); 224 } 225 } 226 227 // Also print the progress percentage 228 printf(" %3d%%", (int)(completionPercentage * 100)); 229 230 fflush(stdout); 231 } 232 233 234 void 235 PackageManager::ProgressPackageDownloadComplete(const char* packageName) 236 { 237 if (fInteractive) { 238 // Overwrite the progress bar with whitespace 239 printf("\r"); 240 struct winsize w; 241 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); 242 for (int i = 0; i < (w.ws_col); i++) 243 printf(" "); 244 printf("\r\x1b[1A"); // Go to previous line. 245 } 246 247 printf("Downloading %s...done.\n", packageName); 248 } 249 250 251 void 252 PackageManager::ProgressPackageChecksumStarted(const char* title) 253 { 254 printf("%s...", title); 255 } 256 257 258 void 259 PackageManager::ProgressPackageChecksumComplete(const char* title) 260 { 261 printf("done.\n"); 262 } 263 264 265 void 266 PackageManager::ProgressStartApplyingChanges(InstalledRepository& repository) 267 { 268 printf("[%s] Applying changes ...\n", repository.Name().String()); 269 } 270 271 272 void 273 PackageManager::ProgressTransactionCommitted(InstalledRepository& repository, 274 const BCommitTransactionResult& result) 275 { 276 const char* repositoryName = repository.Name().String(); 277 278 int32 issueCount = result.CountIssues(); 279 for (int32 i = 0; i < issueCount; i++) { 280 const BTransactionIssue* issue = result.IssueAt(i); 281 if (issue->PackageName().IsEmpty()) { 282 printf("[%s] warning: %s\n", repositoryName, 283 issue->ToString().String()); 284 } else { 285 printf("[%s] warning: package %s: %s\n", repositoryName, 286 issue->PackageName().String(), issue->ToString().String()); 287 } 288 } 289 290 printf("[%s] Changes applied. Old activation state backed up in \"%s\"\n", 291 repositoryName, result.OldStateDirectory().String()); 292 printf("[%s] Cleaning up ...\n", repositoryName); 293 } 294 295 296 void 297 PackageManager::ProgressApplyingChangesDone(InstalledRepository& repository) 298 { 299 printf("[%s] Done.\n", repository.Name().String()); 300 } 301 302 303 void 304 PackageManager::_PrintResult(InstalledRepository& installationRepository) 305 { 306 if (!installationRepository.HasChanges()) 307 return; 308 309 printf(" in %s:\n", installationRepository.Name().String()); 310 311 PackageList& packagesToActivate 312 = installationRepository.PackagesToActivate(); 313 PackageList& packagesToDeactivate 314 = installationRepository.PackagesToDeactivate(); 315 316 BStringList upgradedPackages; 317 BStringList upgradedPackageVersions; 318 for (int32 i = 0; 319 BSolverPackage* installPackage = packagesToActivate.ItemAt(i); 320 i++) { 321 for (int32 j = 0; 322 BSolverPackage* uninstallPackage = packagesToDeactivate.ItemAt(j); 323 j++) { 324 if (installPackage->Info().Name() == uninstallPackage->Info().Name()) { 325 upgradedPackages.Add(installPackage->Info().Name()); 326 upgradedPackageVersions.Add(uninstallPackage->Info().Version().ToString()); 327 break; 328 } 329 } 330 } 331 332 for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i); 333 i++) { 334 BString repository; 335 if (dynamic_cast<MiscLocalRepository*>(package->Repository()) != NULL) 336 repository = "local file"; 337 else 338 repository.SetToFormat("repository %s", package->Repository()->Name().String()); 339 340 int position = upgradedPackages.IndexOf(package->Info().Name()); 341 if (position >= 0) { 342 printf(" upgrade package %s-%s to %s from %s\n", 343 package->Info().Name().String(), 344 upgradedPackageVersions.StringAt(position).String(), 345 package->Info().Version().ToString().String(), 346 repository.String()); 347 } else { 348 printf(" install package %s-%s from %s\n", 349 package->Info().Name().String(), 350 package->Info().Version().ToString().String(), 351 repository.String()); 352 } 353 } 354 355 for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i); 356 i++) { 357 if (upgradedPackages.HasString(package->Info().Name())) 358 continue; 359 printf(" uninstall package %s\n", package->VersionedName().String()); 360 } 361 // TODO: Print file/download sizes. Unfortunately our package infos don't 362 // contain the file size. Which is probably correct. The file size (and possibly 363 // other information) should, however, be provided by the repository cache in 364 // some way. Extend BPackageInfo? Create a BPackageFileInfo? 365 } 366