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