1 /* 2 * Copyright 2017-2020, 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 <sys/stat.h> 10 #include <time.h> 11 12 #include <AutoLocker.h> 13 #include <Catalog.h> 14 #include <FileIO.h> 15 #include <support/StopWatch.h> 16 #include <support/ZlibCompressionAlgorithm.h> 17 18 #include "HaikuDepotConstants.h" 19 #include "Logger.h" 20 #include "ServerHelper.h" 21 #include "ServerSettings.h" 22 #include "StorageUtils.h" 23 #include "TarArchiveService.h" 24 25 26 #undef B_TRANSLATION_CONTEXT 27 #define B_TRANSLATION_CONTEXT "ServerIconExportUpdateProcess" 28 29 30 /*! This constructor will locate the cached data in a standardized location */ 31 32 ServerIconExportUpdateProcess::ServerIconExportUpdateProcess( 33 Model* model, 34 uint32 serverProcessOptions) 35 : 36 AbstractServerProcess(serverProcessOptions), 37 fModel(model), 38 fCountIconsSet(0) 39 { 40 AutoLocker<BLocker> locker(fModel->Lock()); 41 if (fModel->IconStoragePath(fLocalIconStoragePath) != B_OK) { 42 HDINFO("[%s] unable to obtain the path for storing icons", 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 HDDEBUG("[%s] will populate icons for %" B_PRId32 " depots", Name(), 119 depots.CountItems()) 120 121 for (int32 i = 0; 122 (i < depots.CountItems()) && !WasStopped() && (result == B_OK); 123 i++) { 124 AutoLocker<BLocker> locker(fModel->Lock()); 125 DepotInfo depotInfo = depots.ItemAtFast(i); 126 result = PopulateForDepot(depotInfo); 127 } 128 129 if (Logger::IsInfoEnabled()) { 130 double secs = watch.ElapsedTime() / 1000000.0; 131 HDINFO("[%s] did populate %" B_PRId32 " packages' icons (%6.3g secs)", 132 Name(), fCountIconsSet, secs) 133 } 134 135 return result; 136 } 137 138 139 /*! This method assumes that the model lock has been acquired */ 140 141 status_t 142 ServerIconExportUpdateProcess::PopulateForDepot(const DepotInfo& depot) 143 { 144 HDINFO("[%s] will populate icons for depot [%s]", 145 Name(), depot.Name().String()) 146 status_t result = B_OK; 147 const PackageList& packages = depot.Packages(); 148 for(int32 j = 0; 149 (j < packages.CountItems()) && !WasStopped() && (result == B_OK); 150 j++) { 151 const PackageInfoRef& packageInfoRef = packages.ItemAtFast(j); 152 result = PopulateForPkg(packageInfoRef); 153 154 if (result == B_FILE_NOT_FOUND) 155 result = B_OK; 156 } 157 158 return result; 159 } 160 161 162 /*! This method assumes that the model lock has been acquired */ 163 164 status_t 165 ServerIconExportUpdateProcess::PopulateForPkg(const PackageInfoRef& package) 166 { 167 BPath bestIconPath; 168 169 if ( fLocalIconStore->TryFindIconPath( 170 package->Name(), bestIconPath) == B_OK) { 171 172 BFile bestIconFile(bestIconPath.Path(), O_RDONLY); 173 BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(bestIconFile), true); 174 package->SetIcon(bitmapRef); 175 176 HDDEBUG("[%s] have set the package icon for [%s] from [%s]", 177 Name(), package->Name().String(), bestIconPath.Path()) 178 179 fCountIconsSet++; 180 181 return B_OK; 182 } 183 184 HDDEBUG("[%s] did not set the package icon for [%s]; no data", 185 Name(), package->Name().String()) 186 187 return B_FILE_NOT_FOUND; 188 } 189 190 191 status_t 192 ServerIconExportUpdateProcess::_DownloadAndUnpack() 193 { 194 BPath tarGzFilePath(tmpnam(NULL)); 195 status_t result = B_OK; 196 197 HDINFO("[%s] will start fetching icons", Name()) 198 199 result = _Download(tarGzFilePath); 200 201 switch (result) { 202 case HD_ERR_NOT_MODIFIED: 203 HDINFO("[%s] icons not modified - will use existing", Name()) 204 return result; 205 break; 206 case B_OK: 207 return _Unpack(tarGzFilePath); 208 default: 209 return (_HandleDownloadFailure() != B_OK) ? result : B_OK; 210 } 211 } 212 213 214 /*! if the download failed, but there are existing icons in place to use 215 then use those icons. To detect the existing files, look for the 216 icons' meta-info file. 217 */ 218 219 status_t 220 ServerIconExportUpdateProcess::_HandleDownloadFailure() 221 { 222 bool hasData; 223 status_t result = HasLocalData(&hasData); 224 225 if (result == B_OK) { 226 if (hasData) { 227 HDINFO("[%s] failed to update data, but have old data anyway " 228 "so will carry on with that", Name()) 229 } else { 230 HDINFO("[%s] failed to obtain data", Name()) 231 result = HD_ERR_NO_DATA; 232 } 233 } else { 234 HDERROR("[%s] unable to detect if there is local data\n", Name()) 235 } 236 237 return result; 238 } 239 240 241 /*! The tar-ball data of icons has arrived and so old data needs to be purged 242 to make way for the new data and the new data needs to be unpacked. 243 */ 244 245 status_t 246 ServerIconExportUpdateProcess::_Unpack(BPath& tarGzFilePath) 247 { 248 status_t result; 249 HDINFO("[%s] delete any existing stored data", Name()) 250 StorageUtils::RemoveDirectoryContents(fLocalIconStoragePath); 251 252 BFile *tarGzFile = new BFile(tarGzFilePath.Path(), O_RDONLY); 253 BDataIO* tarIn; 254 255 BZlibDecompressionParameters* zlibDecompressionParameters 256 = new BZlibDecompressionParameters(); 257 258 result = BZlibCompressionAlgorithm() 259 .CreateDecompressingInputStream(tarGzFile, 260 zlibDecompressionParameters, tarIn); 261 262 if (result == B_OK) { 263 BStopWatch watch( 264 "ServerIconExportUpdateProcess::DownloadAndUnpack_Unpack", 265 true); 266 267 result = TarArchiveService::Unpack(*tarIn, 268 fLocalIconStoragePath, NULL); 269 270 if (result == B_OK) { 271 double secs = watch.ElapsedTime() / 1000000.0; 272 HDINFO("[%s] did unpack icon tgz in (%6.3g secs)", Name(), secs) 273 274 if (0 != remove(tarGzFilePath.Path())) { 275 HDERROR("[%s] unable to delete the temporary tgz path; %s", 276 Name(), tarGzFilePath.Path()) 277 } 278 } 279 } 280 281 delete tarGzFile; 282 HDINFO("[%s] did complete unpacking icons", Name()) 283 return result; 284 } 285 286 287 status_t 288 ServerIconExportUpdateProcess::HasLocalData(bool* result) const 289 { 290 BPath path; 291 status_t status = GetStandardMetaDataPath(path); 292 293 if (status != B_OK) 294 return status; 295 296 off_t size; 297 298 status = StorageUtils::ExistsObject(path, result, NULL, &size); 299 300 if (status == B_OK && size == 0) 301 *result = false; 302 303 return status; 304 } 305 306 307 status_t 308 ServerIconExportUpdateProcess::GetStandardMetaDataPath(BPath& path) const 309 { 310 status_t result = fModel->IconStoragePath(path); 311 312 if (result != B_OK) 313 return result; 314 315 path.Append("hicn/info.json"); 316 return B_OK; 317 } 318 319 320 void 321 ServerIconExportUpdateProcess::GetStandardMetaDataJsonPath( 322 BString& jsonPath) const 323 { 324 // the "$" here indicates that the data is at the top level. 325 jsonPath.SetTo("$"); 326 } 327 328 329 status_t 330 ServerIconExportUpdateProcess::_Download(BPath& tarGzFilePath) 331 { 332 return DownloadToLocalFileAtomically(tarGzFilePath, 333 ServerSettings::CreateFullUrl("/__pkgicon/all.tar.gz")); 334 } 335 336 337 338