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