1 /* 2 * Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ServerIconExportUpdateProcess.h" 8 9 #include <stdio.h> 10 #include <sys/stat.h> 11 #include <time.h> 12 13 #include <AutoLocker.h> 14 #include <Catalog.h> 15 #include <FileIO.h> 16 #include <support/StopWatch.h> 17 #include <support/ZlibCompressionAlgorithm.h> 18 19 #include "HaikuDepotConstants.h" 20 #include "Logger.h" 21 #include "ServerHelper.h" 22 #include "ServerSettings.h" 23 #include "StorageUtils.h" 24 #include "TarArchiveService.h" 25 26 27 #undef B_TRANSLATION_CONTEXT 28 #define B_TRANSLATION_CONTEXT "ServerIconExportUpdateProcess" 29 30 31 /*! This constructor will locate the cached data in a standardized location */ 32 33 ServerIconExportUpdateProcess::ServerIconExportUpdateProcess( 34 Model* model, 35 uint32 serverProcessOptions) 36 : 37 AbstractServerProcess(serverProcessOptions), 38 fModel(model), 39 fCountIconsSet(0) 40 { 41 AutoLocker<BLocker> locker(fModel->Lock()); 42 if (fModel->IconStoragePath(fLocalIconStoragePath) != B_OK) { 43 printf("[%s] unable to obtain the path for storing icons\n", Name()); 44 fLocalIconStoragePath.Unset(); 45 fLocalIconStore = NULL; 46 } else { 47 fLocalIconStore = new LocalIconStore(fLocalIconStoragePath); 48 } 49 } 50 51 52 ServerIconExportUpdateProcess::~ServerIconExportUpdateProcess() 53 { 54 delete fLocalIconStore; 55 } 56 57 58 const char* 59 ServerIconExportUpdateProcess::Name() const 60 { 61 return "ServerIconExportUpdateProcess"; 62 } 63 64 65 const char* 66 ServerIconExportUpdateProcess::Description() const 67 { 68 return B_TRANSLATE("Synchronizing icons"); 69 } 70 71 72 status_t 73 ServerIconExportUpdateProcess::RunInternal() 74 { 75 status_t result = B_OK; 76 77 if (NULL == fLocalIconStore || fLocalIconStoragePath.Path() == NULL) 78 result = B_ERROR; 79 80 if (IsSuccess(result) && HasOption(SERVER_PROCESS_DROP_CACHE)) { 81 result = StorageUtils::RemoveDirectoryContents(fLocalIconStoragePath); 82 } 83 84 if (result == B_OK) { 85 bool hasData; 86 87 result = HasLocalData(&hasData); 88 89 if (result == B_OK && ShouldAttemptNetworkDownload(hasData)) 90 result = _DownloadAndUnpack(); 91 92 if (IsSuccess(result)) { 93 status_t hasDataResult = HasLocalData(&hasData); 94 95 if (hasDataResult == B_OK && !hasData) 96 result = HD_ERR_NO_DATA; 97 } 98 } 99 100 if (IsSuccess(result) && !WasStopped()) 101 result = Populate(); 102 103 return result; 104 } 105 106 107 status_t 108 ServerIconExportUpdateProcess::Populate() 109 { 110 BStopWatch watch("ServerIconExportUpdateProcess::Populate", true); 111 DepotList depots = fModel->Depots(); 112 status_t result = B_OK; 113 114 { 115 AutoLocker<BLocker> locker(fModel->Lock()); 116 depots = fModel->Depots(); 117 } 118 119 if (Logger::IsDebugEnabled()) { 120 printf("[%s] will populate icons for %" B_PRId32 " depots\n", Name(), 121 depots.CountItems()); 122 } 123 124 for (int32 i = 0; 125 (i < depots.CountItems()) && !WasStopped() && (result == B_OK); 126 i++) { 127 AutoLocker<BLocker> locker(fModel->Lock()); 128 DepotInfo depotInfo = depots.ItemAtFast(i); 129 result = PopulateForDepot(depotInfo); 130 } 131 132 if (Logger::IsInfoEnabled()) { 133 double secs = watch.ElapsedTime() / 1000000.0; 134 printf("[%s] did populate %" B_PRId32 " packages' icons (%6.3g secs)\n", 135 Name(), fCountIconsSet, secs); 136 } 137 138 return result; 139 } 140 141 142 /*! This method assumes that the model lock has been acquired */ 143 144 status_t 145 ServerIconExportUpdateProcess::PopulateForDepot(const DepotInfo& depot) 146 { 147 printf("[%s] will populate icons for depot [%s]\n", 148 Name(), depot.Name().String()); 149 status_t result = B_OK; 150 const PackageList& packages = depot.Packages(); 151 for(int32 j = 0; 152 (j < packages.CountItems()) && !WasStopped() && (result == B_OK); 153 j++) { 154 const PackageInfoRef& packageInfoRef = packages.ItemAtFast(j); 155 result = PopulateForPkg(packageInfoRef); 156 157 if (result == B_FILE_NOT_FOUND) 158 result = B_OK; 159 } 160 161 return result; 162 } 163 164 165 /*! This method assumes that the model lock has been acquired */ 166 167 status_t 168 ServerIconExportUpdateProcess::PopulateForPkg(const PackageInfoRef& package) 169 { 170 BPath bestIconPath; 171 172 if ( fLocalIconStore->TryFindIconPath( 173 package->Name(), bestIconPath) == B_OK) { 174 175 BFile bestIconFile(bestIconPath.Path(), O_RDONLY); 176 BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(bestIconFile), true); 177 package->SetIcon(bitmapRef); 178 179 if (Logger::IsDebugEnabled()) { 180 printf("[%s] have set the package icon for [%s] from [%s]\n", 181 Name(), package->Name().String(), bestIconPath.Path()); 182 } 183 184 fCountIconsSet++; 185 186 return B_OK; 187 } 188 189 if (Logger::IsDebugEnabled()) { 190 printf("[%s] did not set the package icon for [%s]; no data\n", 191 Name(), package->Name().String()); 192 } 193 194 return B_FILE_NOT_FOUND; 195 } 196 197 198 status_t 199 ServerIconExportUpdateProcess::_DownloadAndUnpack() 200 { 201 BPath tarGzFilePath(tmpnam(NULL)); 202 status_t result = B_OK; 203 204 printf("[%s] will start fetching icons\n", Name()); 205 206 result = _Download(tarGzFilePath); 207 208 switch (result) { 209 case HD_ERR_NOT_MODIFIED: 210 printf("[%s] icons not modified - will use existing\n", Name()); 211 return result; 212 break; 213 case B_OK: 214 return _Unpack(tarGzFilePath); 215 default: 216 return (_HandleDownloadFailure() != B_OK) ? result : B_OK; 217 } 218 } 219 220 221 /*! if the download failed, but there are existing icons in place to use 222 then use those icons. To detect the existing files, look for the 223 icons' meta-info file. 224 */ 225 226 status_t 227 ServerIconExportUpdateProcess::_HandleDownloadFailure() 228 { 229 bool hasData; 230 status_t result = HasLocalData(&hasData); 231 232 if (result == B_OK) { 233 if (hasData) { 234 printf("[%s] failed to update data, but have old data anyway " 235 "so will carry on with that\n", Name()); 236 } else { 237 printf("[%s] failed to obtain data\n", Name()); 238 result = HD_ERR_NO_DATA; 239 } 240 } else { 241 printf("[%s] unable to detect if there is local data\n", Name()); 242 } 243 244 return result; 245 } 246 247 248 /*! The tar-ball data of icons has arrived and so old data needs to be purged 249 to make way for the new data and the new data needs to be unpacked. 250 */ 251 252 status_t 253 ServerIconExportUpdateProcess::_Unpack(BPath& tarGzFilePath) 254 { 255 status_t result; 256 printf("[%s] delete any existing stored data\n", Name()); 257 StorageUtils::RemoveDirectoryContents(fLocalIconStoragePath); 258 259 BFile *tarGzFile = new BFile(tarGzFilePath.Path(), O_RDONLY); 260 BDataIO* tarIn; 261 262 BZlibDecompressionParameters* zlibDecompressionParameters 263 = new BZlibDecompressionParameters(); 264 265 result = BZlibCompressionAlgorithm() 266 .CreateDecompressingInputStream(tarGzFile, 267 zlibDecompressionParameters, tarIn); 268 269 if (result == B_OK) { 270 BStopWatch watch( 271 "ServerIconExportUpdateProcess::DownloadAndUnpack_Unpack", 272 true); 273 274 result = TarArchiveService::Unpack(*tarIn, 275 fLocalIconStoragePath, NULL); 276 277 if (result == B_OK) { 278 double secs = watch.ElapsedTime() / 1000000.0; 279 printf("[%s] did unpack icon tgz in (%6.3g secs)\n", Name(), 280 secs); 281 282 if (0 != remove(tarGzFilePath.Path())) { 283 printf("unable to delete the temporary tgz path; %s\n", 284 tarGzFilePath.Path()); 285 } 286 } 287 } 288 289 delete tarGzFile; 290 printf("[%s] did complete unpacking icons\n", Name()); 291 return result; 292 } 293 294 295 status_t 296 ServerIconExportUpdateProcess::HasLocalData(bool* result) const 297 { 298 BPath path; 299 status_t status = GetStandardMetaDataPath(path); 300 301 if (status != B_OK) 302 return status; 303 304 off_t size; 305 306 status = StorageUtils::ExistsObject(path, result, NULL, &size); 307 308 if (status == B_OK && size == 0) 309 *result = false; 310 311 return status; 312 } 313 314 315 status_t 316 ServerIconExportUpdateProcess::GetStandardMetaDataPath(BPath& path) const 317 { 318 status_t result = fModel->IconStoragePath(path); 319 320 if (result != B_OK) 321 return result; 322 323 path.Append("hicn/info.json"); 324 return B_OK; 325 } 326 327 328 void 329 ServerIconExportUpdateProcess::GetStandardMetaDataJsonPath( 330 BString& jsonPath) const 331 { 332 // the "$" here indicates that the data is at the top level. 333 jsonPath.SetTo("$"); 334 } 335 336 337 status_t 338 ServerIconExportUpdateProcess::_Download(BPath& tarGzFilePath) 339 { 340 return DownloadToLocalFileAtomically(tarGzFilePath, 341 ServerSettings::CreateFullUrl("/__pkgicon/all.tar.gz")); 342 } 343 344 345 346