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 36using namespace BPackageKit; 37using namespace BPackageKit::BManager::BPrivate; 38 39#undef B_TRANSLATION_CONTEXT 40#define B_TRANSLATION_CONTEXT "CheckManager" 41 42 43CheckManager::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 65void 66CheckManager::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 85void 86CheckManager::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 96void 97CheckManager::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 108void 109CheckManager::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 119void 120CheckManager::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 140void 141CheckManager::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 172void 173CheckManager::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 187void 188CheckManager::ProgressPackageDownloadStarted(const char* packageName) 189{ 190 if (fVerbose) 191 _SendNotification(fHeaderChecking.String(), fTextContacting.String()); 192 193 printf("Downloading %s...\n", packageName); 194} 195 196 197void 198CheckManager::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 248void 249CheckManager::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 263void 264CheckManager::ProgressPackageChecksumStarted(const char* title) 265{ 266 if (fVerbose) 267 _SendNotification(fHeaderChecking.String(), title); 268 269 printf("%s...", title); 270} 271 272 273void 274CheckManager::ProgressPackageChecksumComplete(const char* title) 275{ 276 puts("done."); 277} 278 279 280void 281CheckManager::_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 307void 308CheckManager::_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