1 /* 2 * Copyright 2013-2017, 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 * Brian Hill <supernova@tycho.email> 10 */ 11 12 13 #include "CheckManager.h" 14 15 #include <sys/ioctl.h> 16 #include <unistd.h> 17 18 #include <Application.h> 19 #include <Catalog.h> 20 #include <Message.h> 21 #include <Messenger.h> 22 #include <NetworkInterface.h> 23 #include <NetworkRoster.h> 24 #include <NodeInfo.h> 25 #include <Notification.h> 26 #include <Roster.h> 27 28 #include <package/manager/Exceptions.h> 29 #include <package/solver/SolverPackage.h> 30 #include <package/solver/SolverProblem.h> 31 #include <package/solver/SolverProblemSolution.h> 32 33 #include "constants.h" 34 35 using namespace BPackageKit; 36 using namespace BPackageKit::BManager::BPrivate; 37 38 #undef B_TRANSLATION_CONTEXT 39 #define B_TRANSLATION_CONTEXT "CheckManager" 40 41 42 CheckManager::CheckManager(BPackageInstallationLocation location, 43 bool verbose) 44 : 45 BPackageManager(location, &fClientInstallationInterface, this), 46 BPackageManager::UserInteractionHandler(), 47 fClientInstallationInterface(), 48 fVerbose(verbose), 49 fNotificationId("") 50 { 51 if (verbose) { 52 app_info info; 53 if (be_app->GetAppInfo(&info)!= B_OK) 54 fVerbose = false; 55 else 56 fNotificationId << info.team; 57 } 58 fHeaderChecking = B_TRANSLATE("Checking for updates"); 59 fTextContacting = B_TRANSLATE("Contacting software repositories to check " 60 "for package updates."); 61 } 62 63 64 void 65 CheckManager::CheckNetworkConnection() 66 { 67 BNetworkRoster& roster = BNetworkRoster::Default(); 68 BNetworkInterface interface; 69 uint32 cookie = 0; 70 while (roster.GetNextInterface(&cookie, interface) == B_OK) { 71 uint32 flags = interface.Flags(); 72 if ((flags & IFF_LOOPBACK) == 0 73 && (flags & (IFF_UP | IFF_LINK)) == (IFF_UP | IFF_LINK)) { 74 return; 75 } 76 } 77 78 // No network connection detected, cannot continue 79 fputs(B_TRANSLATE("No active network connection was found.\n"), stderr); 80 throw BAbortedByUserException(); 81 } 82 83 84 void 85 CheckManager::JobFailed(BSupportKit::BJob* job) 86 { 87 BString error = job->ErrorString(); 88 if (error.Length() > 0) { 89 error.ReplaceAll("\n", "\n*** "); 90 fprintf(stderr, "%s", error.String()); 91 } 92 } 93 94 95 void 96 CheckManager::JobAborted(BSupportKit::BJob* job) 97 { 98 puts("Job aborted"); 99 BString error = job->ErrorString(); 100 if (error.Length() > 0) { 101 error.ReplaceAll("\n", "\n*** "); 102 fprintf(stderr, "%s", error.String()); 103 } 104 } 105 106 107 void 108 CheckManager::NoUpdatesNotification() 109 { 110 if (fVerbose) { 111 BString header = B_TRANSLATE("No updates available"); 112 BString text = B_TRANSLATE("There were no updates found."); 113 _SendNotification(header.String(), text.String()); 114 } 115 } 116 117 118 void 119 CheckManager::HandleProblems() 120 { 121 puts("Encountered problems:"); 122 123 int32 problemCount = fSolver->CountProblems(); 124 for (int32 i = 0; i < problemCount; i++) { 125 BSolverProblem* problem = fSolver->ProblemAt(i); 126 printf("problem %" B_PRId32 ": %s\n", i + 1, 127 problem->ToString().String()); 128 } 129 130 BString title(B_TRANSLATE("Available updates found")); 131 BString text(B_TRANSLATE("Click here to run SoftwareUpdater. Some updates " 132 "will require a problem solution to be selected.")); 133 _SendNotification(title, text); 134 135 throw BAbortedByUserException(); 136 } 137 138 139 void 140 CheckManager::ConfirmChanges(bool fromMostSpecific) 141 { 142 int32 count = fInstalledRepositories.CountItems(); 143 int32 updateCount = 0; 144 145 if (fromMostSpecific) { 146 for (int32 i = count - 1; i >= 0; i--) 147 _CountUpdates(*fInstalledRepositories.ItemAt(i), updateCount); 148 } else { 149 for (int32 i = 0; i < count; i++) 150 _CountUpdates(*fInstalledRepositories.ItemAt(i), updateCount); 151 } 152 153 printf("Update count=%" B_PRId32 "\n", updateCount); 154 if (updateCount > 0) { 155 BString title(B_TRANSLATE("%count% packages have available updates")); 156 BString count; 157 count << updateCount; 158 title.ReplaceFirst("%count%", count); 159 BString text(B_TRANSLATE("Click here to install updates.")); 160 _SendNotification(title.String(), text.String()); 161 } 162 throw BAbortedByUserException(); 163 } 164 165 166 void 167 CheckManager::Warn(status_t error, const char* format, ...) 168 { 169 va_list args; 170 va_start(args, format); 171 vfprintf(stderr, format, args); 172 va_end(args); 173 174 if (error == B_OK) 175 puts(""); 176 else 177 printf(": %s\n", strerror(error)); 178 } 179 180 181 void 182 CheckManager::ProgressPackageDownloadStarted(const char* packageName) 183 { 184 if (fVerbose) 185 _SendNotification(fHeaderChecking.String(), fTextContacting.String()); 186 187 printf("Downloading %s...\n", packageName); 188 } 189 190 191 void 192 CheckManager::ProgressPackageDownloadActive(const char* packageName, 193 float completionPercentage, off_t bytes, off_t totalBytes) 194 { 195 static const char* progressChars[] = { 196 "\xE2\x96\x8F", 197 "\xE2\x96\x8E", 198 "\xE2\x96\x8D", 199 "\xE2\x96\x8C", 200 "\xE2\x96\x8B", 201 "\xE2\x96\x8A", 202 "\xE2\x96\x89", 203 "\xE2\x96\x88", 204 }; 205 206 int width = 70; 207 208 struct winsize winSize; 209 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winSize) == 0 210 && winSize.ws_col < 77) { 211 // We need 7 characters for the percent display 212 width = winSize.ws_col - 7; 213 } 214 215 int position; 216 int ipart = (int)(completionPercentage * width); 217 int fpart = (int)(((completionPercentage * width) - ipart) * 8); 218 219 fputs("\r", stdout); // return to the beginning of the line 220 221 for (position = 0; position < width; position++) { 222 if (position < ipart) { 223 // This part is fully downloaded, show a full block 224 fputs(progressChars[7], stdout); 225 } else if (position > ipart) { 226 // This part is not downloaded, show a space 227 fputs(" ", stdout); 228 } else { 229 // This part is partially downloaded 230 fputs(progressChars[fpart], stdout); 231 } 232 } 233 234 // Also print the progress percentage 235 printf(" %3d%%", (int)(completionPercentage * 100)); 236 237 fflush(stdout); 238 239 } 240 241 242 void 243 CheckManager::ProgressPackageDownloadComplete(const char* packageName) 244 { 245 // Overwrite the progress bar with whitespace 246 fputs("\r", stdout); 247 struct winsize w; 248 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); 249 for (int i = 0; i < (w.ws_col); i++) 250 fputs(" ", stdout); 251 fputs("\r\x1b[1A", stdout); // Go to previous line. 252 253 printf("Downloading %s...done.\n", packageName); 254 } 255 256 257 void 258 CheckManager::ProgressPackageChecksumStarted(const char* title) 259 { 260 if (fVerbose) 261 _SendNotification(fHeaderChecking.String(), title); 262 263 printf("%s...", title); 264 } 265 266 267 void 268 CheckManager::ProgressPackageChecksumComplete(const char* title) 269 { 270 puts("done."); 271 } 272 273 274 void 275 CheckManager::_CountUpdates(InstalledRepository& installationRepository, 276 int32& updateCount) 277 { 278 if (!installationRepository.HasChanges()) 279 return; 280 281 PackageList& packagesToActivate 282 = installationRepository.PackagesToActivate(); 283 PackageList& packagesToDeactivate 284 = installationRepository.PackagesToDeactivate(); 285 286 for (int32 i = 0; 287 BSolverPackage* installPackage = packagesToActivate.ItemAt(i); 288 i++) { 289 for (int32 j = 0; 290 BSolverPackage* uninstallPackage = packagesToDeactivate.ItemAt(j); 291 j++) { 292 if (installPackage->Info().Name() == uninstallPackage->Info().Name()) { 293 updateCount++; 294 break; 295 } 296 } 297 } 298 } 299 300 301 void 302 CheckManager::_SendNotification(const char* title, const char* text) 303 { 304 BNotification notification(B_INFORMATION_NOTIFICATION); 305 notification.SetGroup("SoftwareUpdater"); 306 notification.SetTitle(title); 307 notification.SetContent(text); 308 notification.SetOnClickApp(kAppSignature); 309 if(fVerbose) 310 notification.SetMessageID(fNotificationId); 311 notification.Send(); 312 } 313