Project
Loading...
Searching...
No Matches
CcdbApi.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
16
17#include "CCDB/CcdbApi.h"
18#include "CCDB/CCDBQuery.h"
19
25#include <chrono>
26#include <memory>
27#include <ranges>
28#include <sstream>
29#include <TFile.h>
30#include <TGrid.h>
31#include <TSystem.h>
32#include <TStreamerInfo.h>
33#include <TMemFile.h>
34#include <TH1F.h>
35#include <TTree.h>
36#include <fairlogger/Logger.h>
37#include <TError.h>
38#include <TClass.h>
40#include <algorithm>
41#include <filesystem>
42#include <boost/algorithm/string.hpp>
43#include <iostream>
44#include <mutex>
45#include <boost/interprocess/sync/named_semaphore.hpp>
46#include <regex>
47#include <cstdio>
48#include <string>
49#include <TAlienUserAgent.h>
50#include <unordered_set>
51#include "rapidjson/document.h"
52#include "rapidjson/writer.h"
53#include "rapidjson/stringbuffer.h"
54
55namespace o2::ccdb
56{
57
58using namespace std;
59
60std::mutex gIOMutex; // to protect TMemFile IO operations
61unique_ptr<TJAlienCredentials> CcdbApi::mJAlienCredentials = nullptr;
62
70{
71 public:
72 CCDBSemaphore(std::string const& cachepath, std::string const& path);
74
75 private:
76 boost::interprocess::named_semaphore* mSem = nullptr;
77 std::string mSemName{}; // name under which semaphore is kept by the OS kernel
78};
79
80// Small registry class with the purpose that a static object
81// ensures cleanup of registered semaphores even when programs
82// "crash".
84{
85 public:
86 SemaphoreRegistry() = default;
88 void add(CCDBSemaphore const* ptr);
89 void remove(CCDBSemaphore const* ptr);
90
91 private:
92 std::unordered_set<CCDBSemaphore const*> mStore;
93};
94static SemaphoreRegistry gSemaRegistry;
95
97{
98 using namespace o2::framework;
99 setUniqueAgentID();
100
102 mIsCCDBDownloaderPreferred = 0;
103 if (deploymentMode == DeploymentMode::OnlineDDS && deploymentMode == DeploymentMode::OnlineECS && deploymentMode == DeploymentMode::OnlineAUX && deploymentMode == DeploymentMode::FST) {
104 mIsCCDBDownloaderPreferred = 1;
105 }
106 if (getenv("ALICEO2_ENABLE_MULTIHANDLE_CCDBAPI")) { // todo rename ALICEO2_ENABLE_MULTIHANDLE_CCDBAPI to ALICEO2_PREFER_MULTIHANDLE_CCDBAPI
107 mIsCCDBDownloaderPreferred = atoi(getenv("ALICEO2_ENABLE_MULTIHANDLE_CCDBAPI"));
108 }
109 mDownloader = new CCDBDownloader();
110}
111
113{
114 curl_global_cleanup();
115 delete mDownloader;
116}
117
118void CcdbApi::setUniqueAgentID()
119{
120 mUniqueAgentID = TAlienUserAgent::BasedOnEnvironment().ToString();
121}
122
124{
125#ifdef __APPLE__
126 LOG(debug) << "On macOS we simply rely on TGrid::Connect(\"alien\").";
127 return true;
128#endif
129 if (getenv("ALICEO2_CCDB_NOTOKENCHECK") && atoi(getenv("ALICEO2_CCDB_NOTOKENCHECK"))) {
130 return true;
131 }
132 if (getenv("JALIEN_TOKEN_CERT")) {
133 return true;
134 }
135 auto returncode = system("LD_PRELOAD= alien-token-info &> /dev/null");
136 if (returncode == -1) {
137 LOG(error) << "...";
138 }
139 return returncode == 0;
140}
141
142void CcdbApi::curlInit()
143{
144 // todo : are there other things to initialize globally for curl ?
145 curl_global_init(CURL_GLOBAL_DEFAULT);
146 CcdbApi::mJAlienCredentials = std::make_unique<TJAlienCredentials>();
147 CcdbApi::mJAlienCredentials->loadCredentials();
148 CcdbApi::mJAlienCredentials->selectPreferedCredentials();
149
150 // allow to configure the socket timeout of CCDBDownloader (for some tuning studies)
151 if (getenv("ALICEO2_CCDB_SOCKET_TIMEOUT")) {
152 auto timeoutMS = atoi(getenv("ALICEO2_CCDB_SOCKET_TIMEOUT"));
153 if (timeoutMS >= 0) {
154 LOG(info) << "Setting socket timeout to " << timeoutMS << " milliseconds";
155 mDownloader->setKeepaliveTimeoutTime(timeoutMS);
156 }
157 }
158}
159
160void CcdbApi::init(std::string const& host)
161{
162 if (host.empty()) {
163 throw std::invalid_argument("Empty url passed CcdbApi, cannot initialize. Aborting.");
164 }
165
166 // if host is prefixed with "file://" this is a local snapshot
167 // in this case we init the API in snapshot (readonly) mode
168 constexpr const char* SNAPSHOTPREFIX = "file://";
169 mUrl = host;
170
171 if (host.substr(0, 7).compare(SNAPSHOTPREFIX) == 0) {
172 auto path = host.substr(7);
173 initInSnapshotMode(path);
174 } else {
175 initHostsPool(host);
176 curlInit();
177 }
178 // The environment option ALICEO2_CCDB_LOCALCACHE allows
179 // to reduce the number of queries to the server, by collecting the objects in a local
180 // cache folder, and serving from this folder for repeated queries.
181 // This is useful for instance for MC GRID productions in which we spawn
182 // many isolated processes, all querying the CCDB (for potentially the same objects and same timestamp).
183 // In addition, we can monitor exactly which objects are fetched and what is their content.
184 // One can also distribute so obtained caches to sites without network access.
185 //
186 // THE INFORMATION BELOW IS TEMPORARILY WRONG: the functionality of checking the validity if IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE
187 // is NOT set is broken. At the moment the code is modified to behave as if the IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE is always set
188 // whenever the ALICEO2_CCDB_LOCALCACHE is defined.
189 //
190 // When used with the DPL CCDB fetcher (i.e. loadFileToMemory is called), in order to prefer the available snapshot w/o its validity
191 // check an extra variable IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE must be defined, otherwhise the object will be fetched from the
192 // server after the validity check and new snapshot will be created if needed
193
194 std::string snapshotReport{};
195 const char* cachedir = getenv("ALICEO2_CCDB_LOCALCACHE");
196 namespace fs = std::filesystem;
197 if (cachedir) {
198 if (cachedir[0] == 0) {
199 mSnapshotCachePath = fs::weakly_canonical(fs::absolute("."));
200 } else {
201 mSnapshotCachePath = fs::weakly_canonical(fs::absolute(cachedir));
202 }
203 snapshotReport = fmt::format("(cache snapshots to dir={}", mSnapshotCachePath);
204 }
205 if (cachedir) { // || getenv("IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE")) {
206 mPreferSnapshotCache = true;
207 if (mSnapshotCachePath.empty()) {
208 LOGP(fatal, "IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE is defined but the ALICEO2_CCDB_LOCALCACHE is not");
209 }
210 snapshotReport += ", prefer if available";
211 }
212 if (!snapshotReport.empty()) {
213 snapshotReport += ')';
214 }
215
216 mNeedAlienToken = (host.find("https://") != std::string::npos) || (host.find("alice-ccdb.cern.ch") != std::string::npos) || (host.find("ccdb-test.cern.ch") != std::string::npos);
217 // Set the curl timeout. It can be forced with an env var or it has different defaults based on the deployment mode.
218 if (getenv("ALICEO2_CCDB_CURL_TIMEOUT_DOWNLOAD")) {
219 auto timeout = atoi(getenv("ALICEO2_CCDB_CURL_TIMEOUT_DOWNLOAD"));
220 if (timeout >= 0) { // if valid int
221 mCurlTimeoutDownload = timeout;
222 }
223 } else { // set a default depending on the deployment mode
225 if (deploymentMode == o2::framework::DeploymentMode::OnlineDDS ||
228 mCurlTimeoutDownload = 15;
229 } else if (deploymentMode == o2::framework::DeploymentMode::Grid ||
230 deploymentMode == o2::framework::DeploymentMode::FST) {
231 mCurlTimeoutDownload = 15;
232 } else if (deploymentMode == o2::framework::DeploymentMode::Local) {
233 mCurlTimeoutDownload = 5;
234 }
235 }
236
237 if (getenv("ALICEO2_CCDB_CURL_TIMEOUT_UPLOAD")) {
238 auto timeout = atoi(getenv("ALICEO2_CCDB_CURL_TIMEOUT_UPLOAD"));
239 if (timeout >= 0) { // if valid int
240 mCurlTimeoutUpload = timeout;
241 }
242 } else { // set a default depending on the deployment mode
244 if (deploymentMode == o2::framework::DeploymentMode::OnlineDDS ||
247 mCurlTimeoutUpload = 3;
248 } else if (deploymentMode == o2::framework::DeploymentMode::Grid ||
249 deploymentMode == o2::framework::DeploymentMode::FST) {
250 mCurlTimeoutUpload = 20;
251 } else if (deploymentMode == o2::framework::DeploymentMode::Local) {
252 mCurlTimeoutUpload = 20;
253 }
254 }
255 if (mDownloader) {
256 mDownloader->setRequestTimeoutTime(mCurlTimeoutDownload * 1000L);
257 }
258
259 LOGP(debug, "Curl timeouts are set to: download={:2}, upload={:2} seconds", mCurlTimeoutDownload, mCurlTimeoutUpload);
260
261 LOGP(info, "Init CcdApi with UserAgentID: {}, Host: {}{}, Curl timeouts: upload:{} download:{}", mUniqueAgentID, host,
262 mInSnapshotMode ? "(snapshot readonly mode)" : snapshotReport.c_str(), mCurlTimeoutUpload, mCurlTimeoutDownload);
263}
264
266{
267 mDownloader->runLoop(noWait);
268}
269
270// A helper function used in a few places. Updates a ROOT file with meta/header information.
271void CcdbApi::updateMetaInformationInLocalFile(std::string const& filename, std::map<std::string, std::string> const* headers, CCDBQuery const* querysummary)
272{
273 std::lock_guard<std::mutex> guard(gIOMutex);
274 auto oldlevel = gErrorIgnoreLevel;
275 gErrorIgnoreLevel = 6001; // ignoring error messages here (since we catch with IsZombie)
276 TFile snapshotfile(filename.c_str(), "UPDATE");
277 // The assumption is that the blob is a ROOT file
278 if (!snapshotfile.IsZombie()) {
279 if (querysummary && !snapshotfile.Get(CCDBQUERY_ENTRY)) {
280 snapshotfile.WriteObjectAny(querysummary, TClass::GetClass(typeid(*querysummary)), CCDBQUERY_ENTRY);
281 }
282 if (headers && !snapshotfile.Get(CCDBMETA_ENTRY)) {
283 snapshotfile.WriteObjectAny(headers, TClass::GetClass(typeid(*headers)), CCDBMETA_ENTRY);
284 }
285 snapshotfile.Write();
286 snapshotfile.Close();
287 }
288 gErrorIgnoreLevel = oldlevel;
289}
290
296std::string sanitizeObjectName(const std::string& objectName)
297{
298 std::string tmpObjectName = objectName;
299 tmpObjectName.erase(std::remove_if(tmpObjectName.begin(), tmpObjectName.end(),
300 [](auto const& c) -> bool { return (!std::isalnum(c) && c != '_' && c != '/' && c != '.'); }),
301 tmpObjectName.end());
302 return tmpObjectName;
303}
304
305std::unique_ptr<std::vector<char>> CcdbApi::createObjectImage(const void* obj, std::type_info const& tinfo, CcdbObjectInfo* info)
306{
307 // Create a binary image of the object, if CcdbObjectInfo pointer is provided, register there
308 // the assigned object class name and the filename
309 std::lock_guard<std::mutex> guard(gIOMutex);
310 std::string className = o2::utils::MemFileHelper::getClassName(tinfo);
311 std::string tmpFileName = generateFileName(className);
312 if (info) {
313 info->setFileName(tmpFileName);
314 info->setObjectType(className);
315 }
316 return o2::utils::MemFileHelper::createFileImage(obj, tinfo, tmpFileName, CCDBOBJECT_ENTRY);
317}
318
319std::unique_ptr<std::vector<char>> CcdbApi::createObjectImage(const TObject* rootObject, CcdbObjectInfo* info)
320{
321 // Create a binary image of the object, if CcdbObjectInfo pointer is provided, register there
322 // the assigned object class name and the filename
323 std::string className = rootObject->GetName();
324 std::string tmpFileName = generateFileName(className);
325 if (info) {
326 info->setFileName(tmpFileName);
327 info->setObjectType("TObject"); // why TObject and not the actual name?
328 }
329 std::lock_guard<std::mutex> guard(gIOMutex);
330 return o2::utils::MemFileHelper::createFileImage(*rootObject, tmpFileName, CCDBOBJECT_ENTRY);
331}
332
333int CcdbApi::storeAsTFile_impl(const void* obj, std::type_info const& tinfo, std::string const& path,
334 std::map<std::string, std::string> const& metadata,
335 long startValidityTimestamp, long endValidityTimestamp,
336 std::vector<char>::size_type maxSize) const
337{
338 // We need the TClass for this type; will verify if dictionary exists
339 if (!obj) {
340 LOGP(error, "nullptr is provided for object {}/{}/{}", path, startValidityTimestamp, endValidityTimestamp);
341 return -1;
342 }
343 CcdbObjectInfo info;
344 auto img = createObjectImage(obj, tinfo, &info);
345 return storeAsBinaryFile(img->data(), img->size(), info.getFileName(), info.getObjectType(),
346 path, metadata, startValidityTimestamp, endValidityTimestamp, maxSize);
347}
348
349int CcdbApi::storeAsBinaryFile(const char* buffer, size_t size, const std::string& filename, const std::string& objectType,
350 const std::string& path, const std::map<std::string, std::string>& metadata,
351 long startValidityTimestamp, long endValidityTimestamp, std::vector<char>::size_type maxSize) const
352{
353 if (maxSize > 0 && size > maxSize) {
354 LOGP(alarm, "Object will not be uploaded to {} since its size {} exceeds max allowed {}", path, size, maxSize);
355 return -1;
356 }
357 int returnValue = 0;
358
359 // Prepare URL
360 long sanitizedStartValidityTimestamp = startValidityTimestamp;
361 if (startValidityTimestamp == -1) {
362 LOGP(info, "Start of Validity not set, current timestamp used.");
363 sanitizedStartValidityTimestamp = getCurrentTimestamp();
364 }
365 long sanitizedEndValidityTimestamp = endValidityTimestamp;
366 if (endValidityTimestamp == -1) {
367 LOGP(info, "End of Validity not set, start of validity plus 1 day used.");
368 sanitizedEndValidityTimestamp = getFutureTimestamp(60 * 60 * 24 * 1);
369 }
370 if (mInSnapshotMode) { // write local file
371 if (filename.empty() || buffer == nullptr || size == 0) {
372 LOGP(alarm, "Snapshot mode does not support headers-only upload");
373 return -3;
374 }
375 auto pthLoc = getSnapshotDir(mSnapshotTopPath, path);
377 auto flLoc = getSnapshotFile(mSnapshotTopPath, path, filename);
378 // add the timestamps to the end
379 auto pent = flLoc.find_last_of('.');
380 if (pent == std::string::npos) {
381 pent = flLoc.size();
382 }
383 flLoc.insert(pent, fmt::format("_{}_{}", startValidityTimestamp, endValidityTimestamp));
384 ofstream outf(flLoc.c_str(), ios::out | ios::binary);
385 outf.write(buffer, size);
386 outf.close();
387 if (!outf.good()) {
388 throw std::runtime_error(fmt::format("Failed to write local CCDB file {}", flLoc));
389 } else {
390 std::map<std::string, std::string> metaheader(metadata);
391 // add time validity information
392 metaheader["Valid-From"] = std::to_string(startValidityTimestamp);
393 metaheader["Valid-Until"] = std::to_string(endValidityTimestamp);
394 updateMetaInformationInLocalFile(flLoc.c_str(), &metaheader);
395 std::string metaStr{};
396 for (const auto& mentry : metadata) {
397 metaStr += fmt::format("{}={};", mentry.first, mentry.second);
398 }
399 metaStr += "$USER_META;";
400 LOGP(info, "Created local snapshot {}", flLoc);
401 LOGP(info, R"(Upload with: o2-ccdb-upload --host "$ccdbhost" -p {} -f {} -k {} --starttimestamp {} --endtimestamp {} -m "{}")",
402 path, flLoc, CCDBOBJECT_ENTRY, startValidityTimestamp, endValidityTimestamp, metaStr);
403 }
404 return returnValue;
405 }
406
407 // Curl preparation
408 CURL* curl = nullptr;
409 curl = curl_easy_init();
410
411 // checking that all metadata keys do not contain invalid characters
412 checkMetadataKeys(metadata);
413
414 if (curl != nullptr) {
415 auto mime = curl_mime_init(curl);
416 auto field = curl_mime_addpart(mime);
417 curl_mime_name(field, "send");
418 if (!filename.empty()) {
419 curl_mime_filedata(field, filename.c_str());
420 }
421 if (buffer != nullptr && size > 0) {
422 curl_mime_data(field, buffer, size);
423 } else {
424 curl_mime_data(field, "", 0);
425 }
426
427 struct curl_slist* headerlist = nullptr;
428 static const char buf[] = "Expect:";
429 headerlist = curl_slist_append(headerlist, buf);
430
431 curlSetSSLOptions(curl);
432
433 curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
434 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
435 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
436 curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str());
437 curl_easy_setopt(curl, CURLOPT_TIMEOUT, mCurlTimeoutUpload);
438
439 CURLcode res = CURL_LAST;
440
441 for (size_t hostIndex = 0; hostIndex < hostsPool.size() && res > 0; hostIndex++) {
442 std::string fullUrl = getFullUrlForStorage(curl, path, objectType, metadata, sanitizedStartValidityTimestamp, sanitizedEndValidityTimestamp, hostIndex);
443 LOG(debug3) << "Full URL Encoded: " << fullUrl;
444 /* what URL that receives this POST */
445 curl_easy_setopt(curl, CURLOPT_URL, fullUrl.c_str());
446
447 /* Perform the request, res will get the return code */
448 res = CURL_perform(curl);
449 /* Check for errors */
450 if (res != CURLE_OK) {
451 if (res == CURLE_OPERATION_TIMEDOUT) {
452 LOGP(alarm, "curl_easy_perform() timed out. Consider increasing the timeout using the env var `ALICEO2_CCDB_CURL_TIMEOUT_UPLOAD` (seconds), current one is {}", mCurlTimeoutUpload);
453 } else { // generic message
454 LOGP(alarm, "curl_easy_perform() failed: {}", curl_easy_strerror(res));
455 }
456 returnValue = res;
457 }
458 }
459
460 /* always cleanup */
461 curl_easy_cleanup(curl);
462
463 /* free slist */
464 curl_slist_free_all(headerlist);
465 /* free mime */
466 curl_mime_free(mime);
467 } else {
468 LOGP(alarm, "curl initialization failure");
469 returnValue = -2;
470 }
471 return returnValue;
472}
473
474int CcdbApi::storeAsTFile(const TObject* rootObject, std::string const& path, std::map<std::string, std::string> const& metadata,
475 long startValidityTimestamp, long endValidityTimestamp, std::vector<char>::size_type maxSize) const
476{
477 // Prepare file
478 if (!rootObject) {
479 LOGP(error, "nullptr is provided for object {}/{}/{}", path, startValidityTimestamp, endValidityTimestamp);
480 return -1;
481 }
482 CcdbObjectInfo info;
483 auto img = createObjectImage(rootObject, &info);
484 return storeAsBinaryFile(img->data(), img->size(), info.getFileName(), info.getObjectType(), path, metadata, startValidityTimestamp, endValidityTimestamp, maxSize);
485}
486
487std::string CcdbApi::getFullUrlForStorage(CURL* curl, const std::string& path, const std::string& objtype,
488 const std::map<std::string, std::string>& metadata,
489 long startValidityTimestamp, long endValidityTimestamp, int hostIndex) const
490{
491 // Prepare timestamps
492 std::string startValidityString = getTimestampString(startValidityTimestamp < 0 ? getCurrentTimestamp() : startValidityTimestamp);
493 std::string endValidityString = getTimestampString(endValidityTimestamp < 0 ? getFutureTimestamp(60 * 60 * 24 * 1) : endValidityTimestamp);
494 // Get url
495 std::string url = getHostUrl(hostIndex);
496 // Build URL
497 std::string fullUrl = url + "/" + path + "/" + startValidityString + "/" + endValidityString + "/";
498 // Add type as part of metadata
499 // we need to URL encode the object type, since in case it has special characters (like the "<", ">" for templated classes) it won't work otherwise
500 char* objtypeEncoded = curl_easy_escape(curl, objtype.c_str(), objtype.size());
501 fullUrl += "ObjectType=" + std::string(objtypeEncoded) + "/";
502 curl_free(objtypeEncoded);
503 // Add general metadata
504 for (auto& kv : metadata) {
505 std::string mfirst = kv.first;
506 std::string msecond = kv.second;
507 // same trick for the metadata as for the object type
508 char* mfirstEncoded = curl_easy_escape(curl, mfirst.c_str(), mfirst.size());
509 char* msecondEncoded = curl_easy_escape(curl, msecond.c_str(), msecond.size());
510 fullUrl += std::string(mfirstEncoded) + "=" + std::string(msecondEncoded) + "/";
511 curl_free(mfirstEncoded);
512 curl_free(msecondEncoded);
513 }
514 return fullUrl;
515}
516
517// todo make a single method of the one above and below
518std::string CcdbApi::getFullUrlForRetrieval(CURL* curl, const std::string& path, const std::map<std::string, std::string>& metadata, long timestamp, int hostIndex) const
519{
520 if (mInSnapshotMode) {
521 return getSnapshotFile(mSnapshotTopPath, path);
522 }
523
524 // Prepare timestamps
525 std::string validityString = getTimestampString(timestamp < 0 ? getCurrentTimestamp() : timestamp);
526 // Get host url
527 std::string hostUrl = getHostUrl(hostIndex);
528 // Build URL
529 std::string fullUrl = hostUrl + "/" + path + "/" + validityString + "/";
530 // Add metadata
531 for (auto& kv : metadata) {
532 std::string mfirst = kv.first;
533 std::string msecond = kv.second;
534 // trick for the metadata in case it contains special characters
535 char* mfirstEncoded = curl_easy_escape(curl, mfirst.c_str(), mfirst.size());
536 char* msecondEncoded = curl_easy_escape(curl, msecond.c_str(), msecond.size());
537 fullUrl += std::string(mfirstEncoded) + "=" + std::string(msecondEncoded) + "/";
538 curl_free(mfirstEncoded);
539 curl_free(msecondEncoded);
540 }
541 return fullUrl;
542}
543
548 char* memory;
549 unsigned int size;
550};
551
561static size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp)
562{
563 size_t realsize = size * nmemb;
564 auto* mem = (struct MemoryStruct*)userp;
565
566 mem->memory = (char*)realloc(mem->memory, mem->size + realsize + 1);
567 if (mem->memory == nullptr) {
568 printf("not enough memory (realloc returned NULL)\n");
569 return 0;
570 }
571
572 memcpy(&(mem->memory[mem->size]), contents, realsize);
573 mem->size += realsize;
574 mem->memory[mem->size] = 0;
575
576 return realsize;
577}
578
590static size_t WriteToFileCallback(void* ptr, size_t size, size_t nmemb, FILE* stream)
591{
592 size_t written = fwrite(ptr, size, nmemb, stream);
593 return written;
594}
595
603static CURLcode ssl_ctx_callback(CURL*, void*, void* parm)
604{
605 std::string msg((const char*)parm);
606 int start = 0, end = msg.find('\n');
607
608 if (msg.length() > 0 && end == -1) {
609 LOG(warn) << msg;
610 } else if (end > 0) {
611 while (end > 0) {
612 LOG(warn) << msg.substr(start, end - start);
613 start = end + 1;
614 end = msg.find('\n', start);
615 }
616 }
617 return CURLE_OK;
618}
619
620void CcdbApi::curlSetSSLOptions(CURL* curl_handle)
621{
622 CredentialsKind cmk = mJAlienCredentials->getPreferedCredentials();
623
624 /* NOTE: return early, the warning should be printed on SSL callback if needed */
625 if (cmk == cNOT_FOUND) {
626 return;
627 }
628
629 TJAlienCredentialsObject cmo = mJAlienCredentials->get(cmk);
630
631 char* CAPath = getenv("X509_CERT_DIR");
632 if (CAPath) {
633 curl_easy_setopt(curl_handle, CURLOPT_CAPATH, CAPath);
634 }
635 curl_easy_setopt(curl_handle, CURLOPT_CAINFO, nullptr);
636 curl_easy_setopt(curl_handle, CURLOPT_SSLCERT, cmo.certpath.c_str());
637 curl_easy_setopt(curl_handle, CURLOPT_SSLKEY, cmo.keypath.c_str());
638
639 // NOTE: for lazy logging only
640 curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_FUNCTION, ssl_ctx_callback);
641 curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_DATA, mJAlienCredentials->getMessages().c_str());
642
643 // CURLcode ret = curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_FUNCTION, *ssl_ctx_callback);
644}
645
646using CurlWriteCallback = size_t (*)(void*, size_t, size_t, void*);
647
648void CcdbApi::initCurlOptionsForRetrieve(CURL* curlHandle, void* chunk, CurlWriteCallback writeCallback, bool followRedirect) const
649{
650 curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, writeCallback);
651 curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, chunk);
652 curl_easy_setopt(curlHandle, CURLOPT_FOLLOWLOCATION, followRedirect ? 1L : 0L);
653}
654
655namespace
656{
657template <typename MapType = std::map<std::string, std::string>>
658size_t header_map_callback(char* buffer, size_t size, size_t nitems, void* userdata)
659{
660 auto* headers = static_cast<MapType*>(userdata);
661 auto header = std::string(buffer, size * nitems);
662 std::string::size_type index = header.find(':', 0);
663 if (index != std::string::npos) {
664 const auto key = boost::algorithm::trim_copy(header.substr(0, index));
665 const auto value = boost::algorithm::trim_copy(header.substr(index + 1));
666 LOGP(debug, "Adding #{} {} -> {}", headers->size(), key, value);
667 bool insert = true;
668 if (key == "Content-Length") {
669 auto cl = headers->find("Content-Length");
670 if (cl != headers->end()) {
671 if (std::stol(cl->second) < stol(value)) {
672 headers->erase(key);
673 } else {
674 insert = false;
675 }
676 }
677 }
678
679 // Keep only the first ETag encountered
680 if (key == "ETag") {
681 auto cl = headers->find("ETag");
682 if (cl != headers->end()) {
683 insert = false;
684 }
685 }
686
687 // Keep only the first Content-Type encountered
688 if (key == "Content-Type") {
689 auto cl = headers->find("Content-Type");
690 if (cl != headers->end()) {
691 insert = false;
692 }
693 }
694
695 if (insert) {
696 headers->insert(std::make_pair(key, value));
697 }
698 }
699 return size * nitems;
700}
701} // namespace
702
703void CcdbApi::initCurlHTTPHeaderOptionsForRetrieve(CURL* curlHandle, curl_slist*& option_list, long timestamp, std::map<std::string, std::string>* headers, std::string const& etag,
704 const std::string& createdNotAfter, const std::string& createdNotBefore) const
705{
706 // struct curl_slist* list = nullptr;
707 if (!etag.empty()) {
708 option_list = curl_slist_append(option_list, ("If-None-Match: " + etag).c_str());
709 }
710
711 if (!createdNotAfter.empty()) {
712 option_list = curl_slist_append(option_list, ("If-Not-After: " + createdNotAfter).c_str());
713 }
714
715 if (!createdNotBefore.empty()) {
716 option_list = curl_slist_append(option_list, ("If-Not-Before: " + createdNotBefore).c_str());
717 }
718
719 if (headers != nullptr) {
720 option_list = curl_slist_append(option_list, ("If-None-Match: " + to_string(timestamp)).c_str());
721 curl_easy_setopt(curlHandle, CURLOPT_HEADERFUNCTION, header_map_callback<>);
722 curl_easy_setopt(curlHandle, CURLOPT_HEADERDATA, headers);
723 }
724
725 if (option_list) {
726 curl_easy_setopt(curlHandle, CURLOPT_HTTPHEADER, option_list);
727 }
728
729 curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, mUniqueAgentID.c_str());
730}
731
732bool CcdbApi::receiveToFile(FILE* fileHandle, std::string const& path, std::map<std::string, std::string> const& metadata,
733 long timestamp, std::map<std::string, std::string>* headers, std::string const& etag,
734 const std::string& createdNotAfter, const std::string& createdNotBefore, bool followRedirect) const
735{
736 return receiveObject((void*)fileHandle, path, metadata, timestamp, headers, etag, createdNotAfter, createdNotBefore, followRedirect, (CurlWriteCallback)&WriteToFileCallback);
737}
738
739bool CcdbApi::receiveToMemory(void* chunk, std::string const& path, std::map<std::string, std::string> const& metadata,
740 long timestamp, std::map<std::string, std::string>* headers, std::string const& etag,
741 const std::string& createdNotAfter, const std::string& createdNotBefore, bool followRedirect) const
742{
743 return receiveObject((void*)chunk, path, metadata, timestamp, headers, etag, createdNotAfter, createdNotBefore, followRedirect, (CurlWriteCallback)&WriteMemoryCallback);
744}
745
746bool CcdbApi::receiveObject(void* dataHolder, std::string const& path, std::map<std::string, std::string> const& metadata,
747 long timestamp, std::map<std::string, std::string>* headers, std::string const& etag,
748 const std::string& createdNotAfter, const std::string& createdNotBefore, bool followRedirect, CurlWriteCallback writeCallback) const
749{
750 CURL* curlHandle;
751
752 curlHandle = curl_easy_init();
753 curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, mUniqueAgentID.c_str());
754
755 if (curlHandle != nullptr) {
756
757 curlSetSSLOptions(curlHandle);
758 initCurlOptionsForRetrieve(curlHandle, dataHolder, writeCallback, followRedirect);
759 curl_slist* option_list = nullptr;
760 initCurlHTTPHeaderOptionsForRetrieve(curlHandle, option_list, timestamp, headers, etag, createdNotAfter, createdNotBefore);
761
762 long responseCode = 0;
763 CURLcode curlResultCode = CURL_LAST;
764
765 for (size_t hostIndex = 0; hostIndex < hostsPool.size() && (responseCode >= 400 || curlResultCode > 0); hostIndex++) {
766 std::string fullUrl = getFullUrlForRetrieval(curlHandle, path, metadata, timestamp, hostIndex);
767 curl_easy_setopt(curlHandle, CURLOPT_URL, fullUrl.c_str());
768
769 curlResultCode = CURL_perform(curlHandle);
770
771 if (curlResultCode != CURLE_OK) {
772 LOGP(alarm, "curl_easy_perform() failed: {}", curl_easy_strerror(curlResultCode));
773 } else {
774 curlResultCode = curl_easy_getinfo(curlHandle, CURLINFO_RESPONSE_CODE, &responseCode);
775 if ((curlResultCode == CURLE_OK) && (responseCode < 300)) {
776 curl_slist_free_all(option_list);
777 curl_easy_cleanup(curlHandle);
778 return true;
779 } else {
780 if (curlResultCode != CURLE_OK) {
781 LOGP(alarm, "invalid URL {}", fullUrl);
782 } else {
783 LOGP(alarm, "not found under link {}", fullUrl);
784 }
785 }
786 }
787 }
788
789 curl_slist_free_all(option_list);
790 curl_easy_cleanup(curlHandle);
791 }
792 return false;
793}
794
795TObject* CcdbApi::retrieve(std::string const& path, std::map<std::string, std::string> const& metadata,
796 long timestamp) const
797{
798 struct MemoryStruct chunk {
799 (char*)malloc(1) /*memory*/, 0 /*size*/
800 };
801
802 TObject* result = nullptr;
803
804 bool res = receiveToMemory((void*)&chunk, path, metadata, timestamp);
805
806 if (res) {
807 std::lock_guard<std::mutex> guard(gIOMutex);
808 TMessage mess(kMESS_OBJECT);
809 mess.SetBuffer(chunk.memory, chunk.size, kFALSE);
810 mess.SetReadMode();
811 mess.Reset();
812 result = (TObject*)(mess.ReadObjectAny(mess.GetClass()));
813 if (result == nullptr) {
814 LOGP(info, "couldn't retrieve the object {}", path);
815 }
816 }
817
818 free(chunk.memory);
819
820 return result;
821}
822
823std::string CcdbApi::generateFileName(const std::string& inp)
824{
825 // generate file name for the CCDB object (for now augment the input string by the timestamp)
826 std::string str = inp;
827 str.erase(std::remove_if(str.begin(), str.end(), ::isspace), str.end());
828 str = std::regex_replace(str, std::regex("::"), "-");
830 return str;
831}
832
833TObject* CcdbApi::retrieveFromTFile(std::string const& path, std::map<std::string, std::string> const& metadata,
834 long timestamp, std::map<std::string, std::string>* headers, std::string const& etag,
835 const std::string& createdNotAfter, const std::string& createdNotBefore) const
836{
837 return (TObject*)retrieveFromTFile(typeid(TObject), path, metadata, timestamp, headers, etag, createdNotAfter, createdNotBefore);
838}
839
840bool CcdbApi::retrieveBlob(std::string const& path, std::string const& targetdir, std::map<std::string, std::string> const& metadata,
841 long timestamp, bool preservePath, std::string const& localFileName, std::string const& createdNotAfter, std::string const& createdNotBefore, std::map<std::string, std::string>* outHeaders) const
842{
843
844 // we setup the target path for this blob
845 std::string fulltargetdir = targetdir + (preservePath ? ('/' + path) : "");
846
847 try {
849 } catch (std::exception e) {
850 LOGP(error, "Could not create local snapshot cache directory {}, reason: {}", fulltargetdir, e.what());
851 return false;
852 }
853
855 std::map<std::string, std::string> headers;
856 // avoid creating snapshot via loadFileToMemory itself
857 loadFileToMemory(buff, path, metadata, timestamp, &headers, "", createdNotAfter, createdNotBefore, false);
858 if ((headers.count("Error") != 0) || (buff.empty())) {
859 LOGP(error, "Unable to find object {}/{}, Aborting", path, timestamp);
860 return false;
861 }
862 // determine local filename --> use user given one / default -- or if empty string determine from content
863 auto getFileName = [&headers]() {
864 auto& s = headers["Content-Disposition"];
865 if (s != "") {
866 std::regex re("(.*;)filename=\"(.*)\"");
867 std::cmatch m;
868 if (std::regex_match(s.c_str(), m, re)) {
869 return m[2].str();
870 }
871 }
872 std::string backupname("ccdb-blob.bin");
873 LOG(error) << "Cannot determine original filename from Content-Disposition ... falling back to " << backupname;
874 return backupname;
875 };
876 auto filename = localFileName.size() > 0 ? localFileName : getFileName();
877 std::string targetpath = fulltargetdir + "/" + filename;
878 {
879 std::ofstream objFile(targetpath, std::ios::out | std::ofstream::binary);
880 std::copy(buff.begin(), buff.end(), std::ostreambuf_iterator<char>(objFile));
881 if (!objFile.good()) {
882 LOGP(error, "Unable to open local file {}, Aborting", targetpath);
883 return false;
884 }
885 }
886 CCDBQuery querysummary(path, metadata, timestamp);
887
888 updateMetaInformationInLocalFile(targetpath.c_str(), &headers, &querysummary);
889 if (outHeaders) {
890 *outHeaders = std::move(headers);
891 }
892 return true;
893}
894
895void CcdbApi::snapshot(std::string const& ccdbrootpath, std::string const& localDir, long timestamp) const
896{
897 // query all subpaths to ccdbrootpath
898 const auto allfolders = getAllFolders(ccdbrootpath);
899 std::map<std::string, std::string> metadata;
900 for (auto& folder : allfolders) {
901 retrieveBlob(folder, localDir, metadata, timestamp);
902 }
903}
904
905void* CcdbApi::extractFromTFile(TFile& file, TClass const* cl, const char* what)
906{
907 if (!cl) {
908 return nullptr;
909 }
910 auto object = file.GetObjectChecked(what, cl);
911 if (!object) {
912 // it could be that object was stored with previous convention
913 // where the classname was taken as key
914 std::string objectName(cl->GetName());
915 o2::utils::Str::trim(objectName);
916 object = file.GetObjectChecked(objectName.c_str(), cl);
917 LOG(warn) << "Did not find object under expected name " << what;
918 if (!object) {
919 return nullptr;
920 }
921 LOG(warn) << "Found object under deprecated name " << cl->GetName();
922 }
923 auto result = object;
924 // We need to handle some specific cases as ROOT ties them deeply
925 // to the file they are contained in
926 if (cl->InheritsFrom("TObject")) {
927 // make a clone
928 // detach from the file
929 auto tree = dynamic_cast<TTree*>((TObject*)object);
930 if (tree) {
931 tree->LoadBaskets(0x1L << 32); // make tree memory based
932 tree->SetDirectory(nullptr);
933 result = tree;
934 } else {
935 auto h = dynamic_cast<TH1*>((TObject*)object);
936 if (h) {
937 h->SetDirectory(nullptr);
938 result = h;
939 }
940 }
941 }
942 return result;
943}
944
945void* CcdbApi::extractFromLocalFile(std::string const& filename, std::type_info const& tinfo, std::map<std::string, std::string>* headers) const
946{
947 if (!std::filesystem::exists(filename)) {
948 LOG(error) << "Local snapshot " << filename << " not found \n";
949 return nullptr;
950 }
951 std::lock_guard<std::mutex> guard(gIOMutex);
952 auto tcl = tinfo2TClass(tinfo);
953 TFile f(filename.c_str(), "READ");
954 if (headers) {
955 auto storedmeta = retrieveMetaInfo(f);
956 if (storedmeta) {
957 *headers = *storedmeta; // do a simple deep copy
958 delete storedmeta;
959 }
960 if ((isSnapshotMode() || mPreferSnapshotCache) && headers->find("ETag") == headers->end()) { // generate dummy ETag to profit from the caching
961 (*headers)["ETag"] = filename;
962 }
963 if (headers->find("fileSize") == headers->end()) {
964 (*headers)["fileSize"] = fmt::format("{}", f.GetEND());
965 }
966 }
967 return extractFromTFile(f, tcl);
968}
969
970bool CcdbApi::initTGrid() const
971{
972 if (mNeedAlienToken && !gGrid) {
973 static bool allowNoToken = getenv("ALICEO2_CCDB_NOTOKENCHECK") && atoi(getenv("ALICEO2_CCDB_NOTOKENCHECK"));
974 if (!allowNoToken && !checkAlienToken()) {
975 LOG(fatal) << "Alien Token Check failed - Please get an alien token before running with https CCDB endpoint, or alice-ccdb.cern.ch!";
976 }
977 TGrid::Connect("alien");
978 static bool errorShown = false;
979 if (!gGrid && errorShown == false) {
980 if (allowNoToken) {
981 LOG(error) << "TGrid::Connect returned nullptr. May be due to missing alien token";
982 } else {
983 LOG(fatal) << "TGrid::Connect returned nullptr. May be due to missing alien token";
984 }
985 errorShown = true;
986 }
987 }
988 return gGrid != nullptr;
989}
990
991void* CcdbApi::downloadFilesystemContent(std::string const& url, std::type_info const& tinfo, std::map<std::string, std::string>* headers) const
992{
993 if ((url.find("alien:/", 0) != std::string::npos) && !initTGrid()) {
994 return nullptr;
995 }
996 std::lock_guard<std::mutex> guard(gIOMutex);
997 auto memfile = TMemFile::Open(url.c_str(), "OPEN");
998 if (memfile) {
999 auto cl = tinfo2TClass(tinfo);
1000 auto content = extractFromTFile(*memfile, cl);
1001 if (headers && headers->find("fileSize") == headers->end()) {
1002 (*headers)["fileSize"] = fmt::format("{}", memfile->GetEND());
1003 }
1004 delete memfile;
1005 return content;
1006 }
1007 return nullptr;
1008}
1009
1010void* CcdbApi::interpretAsTMemFileAndExtract(char* contentptr, size_t contentsize, std::type_info const& tinfo)
1011{
1012 void* result = nullptr;
1013 Int_t previousErrorLevel = gErrorIgnoreLevel;
1014 gErrorIgnoreLevel = kFatal;
1015 std::lock_guard<std::mutex> guard(gIOMutex);
1016 TMemFile memFile("name", contentptr, contentsize, "READ");
1017 gErrorIgnoreLevel = previousErrorLevel;
1018 if (!memFile.IsZombie()) {
1019 auto tcl = tinfo2TClass(tinfo);
1020 result = extractFromTFile(memFile, tcl);
1021 if (!result) {
1022 LOG(error) << o2::utils::Str::concat_string("Couldn't retrieve object corresponding to ", tcl->GetName(), " from TFile");
1023 }
1024 memFile.Close();
1025 }
1026 return result;
1027}
1028
1029// navigate sequence of URLs until TFile content is found; object is extracted and returned
1030void* CcdbApi::navigateURLsAndRetrieveContent(CURL* curl_handle, std::string const& url, std::type_info const& tinfo, std::map<std::string, std::string>* headers) const
1031{
1032 // a global internal data structure that can be filled with HTTP header information
1033 // static --> to avoid frequent alloc/dealloc as optimization
1034 // not sure if thread_local takes away that benefit
1035 static thread_local std::multimap<std::string, std::string> headerData;
1036
1037 // let's see first of all if the url is something specific that curl cannot handle
1038 if ((url.find("alien:/", 0) != std::string::npos) || (url.find("file:/", 0) != std::string::npos)) {
1039 return downloadFilesystemContent(url, tinfo, headers);
1040 }
1041 // add other final cases here
1042 // example root://
1043
1044 // otherwise make an HTTP/CURL request
1045 // specify URL to get
1046 curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
1047
1048 MemoryStruct chunk{(char*)malloc(1), 0};
1049 initCurlOptionsForRetrieve(curl_handle, (void*)&chunk, WriteMemoryCallback, false);
1050
1051 curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_map_callback<decltype(headerData)>);
1052 headerData.clear();
1053 curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void*)&headerData);
1054
1055 curlSetSSLOptions(curl_handle);
1056
1057 auto res = CURL_perform(curl_handle);
1058 long response_code = -1;
1059 void* content = nullptr;
1060 bool errorflag = false;
1061 if (res == CURLE_OK && curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response_code) == CURLE_OK) {
1062 if (headers) {
1063 for (auto& p : headerData) {
1064 (*headers)[p.first] = p.second;
1065 }
1066 }
1067 if (200 <= response_code && response_code < 300) {
1068 // good response and the content is directly provided and should have been dumped into "chunk"
1069 content = interpretAsTMemFileAndExtract(chunk.memory, chunk.size, tinfo);
1070 if (headers && headers->find("fileSize") == headers->end()) {
1071 (*headers)["fileSize"] = fmt::format("{}", chunk.size);
1072 }
1073 } else if (response_code == 304) {
1074 // this means the object exist but I am not serving
1075 // it since it's already in your possession
1076
1077 // there is nothing to be done here
1078 LOGP(debug, "Object exists but I am not serving it since it's already in your possession");
1079 }
1080 // this is a more general redirection
1081 else if (300 <= response_code && response_code < 400) {
1082 // we try content locations in order of appearance until one succeeds
1083 // 1st: The "Location" field
1084 // 2nd: Possible "Content-Location" fields - Location field
1085
1086 // some locations are relative to the main server so we need to fix/complement them
1087 auto complement_Location = [this](std::string const& loc) {
1088 if (loc[0] == '/') {
1089 // if it's just a path (noticed by trailing '/' we prepend the server url
1090 return getURL() + loc;
1091 }
1092 return loc;
1093 };
1094
1095 std::vector<std::string> locs;
1096 auto iter = headerData.find("Location");
1097 if (iter != headerData.end()) {
1098 locs.push_back(complement_Location(iter->second));
1099 }
1100 // add alternative locations (not yet included)
1101 auto iter2 = headerData.find("Content-Location");
1102 if (iter2 != headerData.end()) {
1103 auto range = headerData.equal_range("Content-Location");
1104 for (auto it = range.first; it != range.second; ++it) {
1105 if (std::find(locs.begin(), locs.end(), it->second) == locs.end()) {
1106 locs.push_back(complement_Location(it->second));
1107 }
1108 }
1109 }
1110 for (auto& l : locs) {
1111 if (l.size() > 0) {
1112 LOG(debug) << "Trying content location " << l;
1113 content = navigateURLsAndRetrieveContent(curl_handle, l, tinfo, headers);
1114 if (content /* or other success marker in future */) {
1115 break;
1116 }
1117 }
1118 }
1119 } else if (response_code == 404) {
1120 LOG(error) << "Requested resource does not exist: " << url;
1121 errorflag = true;
1122 } else {
1123 LOG(error) << "Error in fetching object " << url << ", curl response code:" << response_code;
1124 errorflag = true;
1125 }
1126 // cleanup
1127 if (chunk.memory != nullptr) {
1128 free(chunk.memory);
1129 }
1130 } else {
1131 LOGP(alarm, "Curl request to {} failed with result {}, response code: {}", url, int(res), response_code);
1132 errorflag = true;
1133 }
1134 // indicate that an error occurred ---> used by caching layers (such as CCDBManager)
1135 if (errorflag && headers) {
1136 (*headers)["Error"] = "An error occurred during retrieval";
1137 }
1138 return content;
1139}
1140
1141void* CcdbApi::retrieveFromTFile(std::type_info const& tinfo, std::string const& path,
1142 std::map<std::string, std::string> const& metadata, long timestamp,
1143 std::map<std::string, std::string>* headers, std::string const& etag,
1144 const std::string& createdNotAfter, const std::string& createdNotBefore) const
1145{
1146 if (!mSnapshotCachePath.empty()) {
1147 // protect this sensitive section by a multi-process named semaphore
1148 auto semaphore_barrier = std::make_unique<CCDBSemaphore>(mSnapshotCachePath, path);
1149 std::string logfile = mSnapshotCachePath + "/log";
1150 std::fstream out(logfile, ios_base::out | ios_base::app);
1151 if (out.is_open()) {
1152 out << "CCDB-access[" << getpid() << "] of " << mUniqueAgentID << " to " << path << " timestamp " << timestamp << "\n";
1153 }
1154 auto snapshotfile = getSnapshotFile(mSnapshotCachePath, path);
1155 bool snapshoting = false;
1156 if (!std::filesystem::exists(snapshotfile)) {
1157 snapshoting = true;
1158 out << "CCDB-access[" << getpid() << "] ... " << mUniqueAgentID << " downloading to snapshot " << snapshotfile << "\n";
1159 // if file not already here and valid --> snapshot it
1160 if (!retrieveBlob(path, mSnapshotCachePath, metadata, timestamp)) {
1161 out << "CCDB-access[" << getpid() << "] ... " << mUniqueAgentID << " failed to create directory for " << snapshotfile << "\n";
1162 }
1163 } else {
1164 out << "CCDB-access[" << getpid() << "] ... " << mUniqueAgentID << "serving from local snapshot " << snapshotfile << "\n";
1165 }
1166
1167 auto res = extractFromLocalFile(snapshotfile, tinfo, headers);
1168 if (!snapshoting) { // if snapshot was created at this call, the log was already done
1169 logReading(path, timestamp, headers, "retrieve from snapshot");
1170 }
1171 return res;
1172 }
1173
1174 // normal mode follows
1175
1176 CURL* curl_handle = curl_easy_init();
1177 curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, mUniqueAgentID.c_str());
1178 std::string fullUrl = getFullUrlForRetrieval(curl_handle, path, metadata, timestamp); // todo check if function still works correctly in case mInSnapshotMode
1179 // if we are in snapshot mode we can simply open the file; extract the object and return
1180 if (mInSnapshotMode) {
1181 auto res = extractFromLocalFile(fullUrl, tinfo, headers);
1182 if (res) {
1183 logReading(path, timestamp, headers, "retrieve from snapshot");
1184 }
1185 return res;
1186 }
1187
1188 curl_slist* option_list = nullptr;
1189 initCurlHTTPHeaderOptionsForRetrieve(curl_handle, option_list, timestamp, headers, etag, createdNotAfter, createdNotBefore);
1190 auto content = navigateURLsAndRetrieveContent(curl_handle, fullUrl, tinfo, headers);
1191
1192 for (size_t hostIndex = 1; hostIndex < hostsPool.size() && !(content); hostIndex++) {
1193 fullUrl = getFullUrlForRetrieval(curl_handle, path, metadata, timestamp, hostIndex);
1194 content = navigateURLsAndRetrieveContent(curl_handle, fullUrl, tinfo, headers);
1195 }
1196 if (content) {
1197 logReading(path, timestamp, headers, "retrieve");
1198 }
1199 curl_slist_free_all(option_list);
1200 curl_easy_cleanup(curl_handle);
1201 return content;
1202}
1203
1204size_t CurlWrite_CallbackFunc_StdString2(void* contents, size_t size, size_t nmemb, std::string* s)
1205{
1206 size_t newLength = size * nmemb;
1207 size_t oldLength = s->size();
1208 try {
1209 s->resize(oldLength + newLength);
1210 } catch (std::bad_alloc& e) {
1211 LOG(error) << "memory error when getting data from CCDB";
1212 return 0;
1213 }
1214
1215 std::copy((char*)contents, (char*)contents + newLength, s->begin() + oldLength);
1216 return size * nmemb;
1217}
1218
1219std::string CcdbApi::list(std::string const& path, bool latestOnly, std::string const& returnFormat, long createdNotAfter, long createdNotBefore) const
1220{
1221 CURL* curl;
1222 CURLcode res = CURL_LAST;
1223 std::string result;
1224
1225 curl = curl_easy_init();
1226 if (curl != nullptr) {
1227 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString2);
1228 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result);
1229 curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str());
1230
1231 struct curl_slist* headers = nullptr;
1232 headers = curl_slist_append(headers, (std::string("Accept: ") + returnFormat).c_str());
1233 headers = curl_slist_append(headers, (std::string("Content-Type: ") + returnFormat).c_str());
1234 if (createdNotAfter >= 0) {
1235 headers = curl_slist_append(headers, ("If-Not-After: " + std::to_string(createdNotAfter)).c_str());
1236 }
1237 if (createdNotBefore >= 0) {
1238 headers = curl_slist_append(headers, ("If-Not-Before: " + std::to_string(createdNotBefore)).c_str());
1239 }
1240 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
1241
1242 curlSetSSLOptions(curl);
1243
1244 std::string fullUrl;
1245 // Perform the request, res will get the return code
1246 for (size_t hostIndex = 0; hostIndex < hostsPool.size() && res != CURLE_OK; hostIndex++) {
1247 fullUrl = getHostUrl(hostIndex);
1248 fullUrl += latestOnly ? "/latest/" : "/browse/";
1249 fullUrl += path;
1250 curl_easy_setopt(curl, CURLOPT_URL, fullUrl.c_str());
1251
1252 res = CURL_perform(curl);
1253 if (res != CURLE_OK) {
1254 LOGP(alarm, "CURL_perform() failed: {}", curl_easy_strerror(res));
1255 }
1256 }
1257 curl_slist_free_all(headers);
1258 curl_easy_cleanup(curl);
1259 }
1260
1261 return result;
1262}
1263
1264std::string CcdbApi::getTimestampString(long timestamp) const
1265{
1266 stringstream ss;
1267 ss << timestamp;
1268 return ss.str();
1269}
1270
1271void CcdbApi::deleteObject(std::string const& path, long timestamp) const
1272{
1273 CURL* curl;
1274 CURLcode res;
1275 stringstream fullUrl;
1276 long timestampLocal = timestamp == -1 ? getCurrentTimestamp() : timestamp;
1277
1278 curl = curl_easy_init();
1279 if (curl != nullptr) {
1280 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
1281 curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str());
1282 curlSetSSLOptions(curl);
1283
1284 for (size_t hostIndex = 0; hostIndex < hostsPool.size(); hostIndex++) {
1285 fullUrl << getHostUrl(hostIndex) << "/" << path << "/" << timestampLocal;
1286 curl_easy_setopt(curl, CURLOPT_URL, fullUrl.str().c_str());
1287
1288 // Perform the request, res will get the return code
1289 res = CURL_perform(curl);
1290 if (res != CURLE_OK) {
1291 LOGP(alarm, "CURL_perform() failed: {}", curl_easy_strerror(res));
1292 }
1293 curl_easy_cleanup(curl);
1294 }
1295 }
1296}
1297
1298void CcdbApi::truncate(std::string const& path) const
1299{
1300 CURL* curl;
1301 CURLcode res;
1302 stringstream fullUrl;
1303 for (size_t i = 0; i < hostsPool.size(); i++) {
1304 std::string url = getHostUrl(i);
1305 fullUrl << url << "/truncate/" << path;
1306
1307 curl = curl_easy_init();
1308 curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str());
1309 if (curl != nullptr) {
1310 curl_easy_setopt(curl, CURLOPT_URL, fullUrl.str().c_str());
1311
1312 curlSetSSLOptions(curl);
1313
1314 // Perform the request, res will get the return code
1315 res = CURL_perform(curl);
1316 if (res != CURLE_OK) {
1317 LOGP(alarm, "CURL_perform() failed: {}", curl_easy_strerror(res));
1318 }
1319 curl_easy_cleanup(curl);
1320 }
1321 }
1322}
1323
1324size_t write_data(void*, size_t size, size_t nmemb, void*)
1325{
1326 return size * nmemb;
1327}
1328
1330{
1331 CURL* curl;
1332 CURLcode res = CURL_LAST;
1333 bool result = false;
1334
1335 curl = curl_easy_init();
1336 curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str());
1337 if (curl) {
1338 for (size_t hostIndex = 0; hostIndex < hostsPool.size() && res != CURLE_OK; hostIndex++) {
1339 curl_easy_setopt(curl, CURLOPT_URL, mUrl.data());
1340 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
1341 curlSetSSLOptions(curl);
1342 res = CURL_perform(curl);
1343 result = (res == CURLE_OK);
1344 }
1345
1346 /* always cleanup */
1347 curl_easy_cleanup(curl);
1348 }
1349 return result;
1350}
1351
1352std::vector<std::string> CcdbApi::parseSubFolders(std::string const& reply) const
1353{
1354 // this needs some text filtering
1355 // go through reply line by line until we see "SubFolders:"
1356 std::stringstream ss(reply.c_str());
1357 std::string line;
1358 std::vector<std::string> folders;
1359
1360 size_t numberoflines = std::count(reply.begin(), reply.end(), '\n');
1361 bool inSubFolderSection = false;
1362
1363 for (size_t linenumber = 0; linenumber < numberoflines; ++linenumber) {
1364 std::getline(ss, line);
1365 if (inSubFolderSection && line.size() > 0) {
1366 // remove all white space
1367 folders.push_back(sanitizeObjectName(line));
1368 }
1369
1370 if (line.compare("Subfolders:") == 0) {
1371 inSubFolderSection = true;
1372 }
1373 }
1374 return folders;
1375}
1376
1377namespace
1378{
1379size_t header_callback(char* buffer, size_t size, size_t nitems, void* userdata)
1380{
1381 auto* headers = static_cast<std::vector<std::string>*>(userdata);
1382 auto header = std::string(buffer, size * nitems);
1383 headers->emplace_back(std::string(header.data()));
1384 return size * nitems;
1385}
1386} // namespace
1387
1388bool stdmap_to_jsonfile(std::map<std::string, std::string> const& meta, std::string const& filename)
1389{
1390
1391 // create directory structure if necessary
1392 auto p = std::filesystem::path(filename).parent_path();
1393 if (!std::filesystem::exists(p)) {
1394 std::filesystem::create_directories(p);
1395 }
1396
1397 rapidjson::StringBuffer buffer;
1398 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
1399 writer.StartObject();
1400 for (const auto& pair : meta) {
1401 writer.Key(pair.first.c_str());
1402 writer.String(pair.second.c_str());
1403 }
1404 writer.EndObject();
1405
1406 // Write JSON to file
1407 std::ofstream file(filename);
1408 if (file.is_open()) {
1409 file << buffer.GetString();
1410 file.close();
1411 } else {
1412 return false;
1413 }
1414 return true;
1415}
1416
1417bool jsonfile_to_stdmap(std::map<std::string, std::string>& meta, std::string const& filename)
1418{
1419 // Read JSON from file
1420 std::ifstream file(filename);
1421 if (!file.is_open()) {
1422 std::cerr << "Failed to open file for reading." << std::endl;
1423 return false;
1424 }
1425
1426 std::string jsonStr((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
1427
1428 // Parse JSON
1429 rapidjson::Document document;
1430 document.Parse(jsonStr.c_str());
1431
1432 if (document.HasParseError()) {
1433 std::cerr << "Error parsing JSON" << std::endl;
1434 return false;
1435 }
1436
1437 // Convert JSON to std::map
1438 for (auto itr = document.MemberBegin(); itr != document.MemberEnd(); ++itr) {
1439 meta[itr->name.GetString()] = itr->value.GetString();
1440 }
1441 return true;
1442}
1443
1444std::map<std::string, std::string> CcdbApi::retrieveHeaders(std::string const& path, std::map<std::string, std::string> const& metadata, long timestamp) const
1445{
1446 // lambda that actually does the call to the CCDB server
1447 auto do_remote_header_call = [this, &path, &metadata, timestamp]() -> std::map<std::string, std::string> {
1448 CURL* curl = curl_easy_init();
1449 CURLcode res = CURL_LAST;
1450 std::string fullUrl = getFullUrlForRetrieval(curl, path, metadata, timestamp);
1451 std::map<std::string, std::string> headers;
1452
1453 if (curl != nullptr) {
1454 struct curl_slist* list = nullptr;
1455 list = curl_slist_append(list, ("If-None-Match: " + std::to_string(timestamp)).c_str());
1456
1457 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
1458
1459 /* get us the resource without a body! */
1460 curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
1461 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
1462 curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_map_callback<>);
1463 curl_easy_setopt(curl, CURLOPT_HEADERDATA, &headers);
1464 curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str());
1465
1466 curlSetSSLOptions(curl);
1467
1468 // Perform the request, res will get the return code
1469 long httpCode = 404;
1470 CURLcode getCodeRes = CURL_LAST;
1471 for (size_t hostIndex = 0; hostIndex < hostsPool.size() && (httpCode >= 400 || res > 0 || getCodeRes > 0); hostIndex++) {
1472 curl_easy_setopt(curl, CURLOPT_URL, fullUrl.c_str());
1473 res = CURL_perform(curl);
1474 if (res != CURLE_OK && res != CURLE_UNSUPPORTED_PROTOCOL) {
1475 // We take out the unsupported protocol error because we are only querying
1476 // header info which is returned in any case. Unsupported protocol error
1477 // occurs sometimes because of redirection to alien for blobs.
1478 LOG(error) << "CURL_perform() failed: " << curl_easy_strerror(res);
1479 }
1480 getCodeRes = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
1481 }
1482 if (httpCode == 404) {
1483 headers.clear();
1484 }
1485 curl_easy_cleanup(curl);
1486 }
1487 return headers;
1488 };
1489
1490 if (!mSnapshotCachePath.empty()) {
1491 // protect this sensitive section by a multi-process named semaphore
1492 auto semaphore_barrier = std::make_unique<CCDBSemaphore>(mSnapshotCachePath + std::string("_headers"), path);
1493
1494 std::string logfile = mSnapshotCachePath + "/log";
1495 std::fstream out(logfile, ios_base::out | ios_base::app);
1496 if (out.is_open()) {
1497 out << "CCDB-header-access[" << getpid() << "] of " << mUniqueAgentID << " to " << path << " timestamp " << timestamp << "\n";
1498 }
1499 auto snapshotfile = getSnapshotFile(mSnapshotCachePath, path + "/" + std::to_string(timestamp), "header.json");
1500 if (!std::filesystem::exists(snapshotfile)) {
1501 out << "CCDB-header-access[" << getpid() << "] ... " << mUniqueAgentID << " storing to snapshot " << snapshotfile << "\n";
1502
1503 // if file not already here and valid --> snapshot it
1504 auto meta = do_remote_header_call();
1505
1506 // cache the result
1507 if (!stdmap_to_jsonfile(meta, snapshotfile)) {
1508 LOG(warn) << "Failed to cache the header information to disc";
1509 }
1510 return meta;
1511 } else {
1512 out << "CCDB-header-access[" << getpid() << "] ... " << mUniqueAgentID << "serving from local snapshot " << snapshotfile << "\n";
1513 std::map<std::string, std::string> meta;
1514 if (!jsonfile_to_stdmap(meta, snapshotfile)) {
1515 LOG(warn) << "Failed to read cached information from disc";
1516 return do_remote_header_call();
1517 }
1518 return meta;
1519 }
1520 }
1521 return do_remote_header_call();
1522}
1523
1524bool CcdbApi::getCCDBEntryHeaders(std::string const& url, std::string const& etag, std::vector<std::string>& headers, const std::string& agentID)
1525{
1526 auto curl = curl_easy_init();
1527 headers.clear();
1528 if (!curl) {
1529 return true;
1530 }
1531
1532 struct curl_slist* list = nullptr;
1533 list = curl_slist_append(list, ("If-None-Match: " + etag).c_str());
1534
1535 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
1536
1537 curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
1538 /* get us the resource without a body! */
1539 curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
1540 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
1541 curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);
1542 curl_easy_setopt(curl, CURLOPT_HEADERDATA, &headers);
1543 if (!agentID.empty()) {
1544 curl_easy_setopt(curl, CURLOPT_USERAGENT, agentID.c_str());
1545 }
1546
1547 curlSetSSLOptions(curl);
1548
1549 /* Perform the request */
1550 curl_easy_perform(curl);
1551 long http_code = 404;
1552 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
1553 if (http_code == 304) {
1554 return false;
1555 }
1556 return true;
1557}
1558
1559void CcdbApi::parseCCDBHeaders(std::vector<std::string> const& headers, std::vector<std::string>& pfns, std::string& etag)
1560{
1561 static std::string etagHeader = "ETag: ";
1562 static std::string locationHeader = "Content-Location: ";
1563 for (auto h : headers) {
1564 if (h.find(etagHeader) == 0) {
1565 etag = std::string(h.data() + etagHeader.size());
1566 } else if (h.find(locationHeader) == 0) {
1567 pfns.emplace_back(std::string(h.data() + locationHeader.size(), h.size() - locationHeader.size()));
1568 }
1569 }
1570}
1571
1573{
1574 auto object = file.GetObjectChecked(CCDBQUERY_ENTRY, TClass::GetClass(typeid(o2::ccdb::CCDBQuery)));
1575 if (object) {
1576 return static_cast<CCDBQuery*>(object);
1577 }
1578 return nullptr;
1579}
1580
1581std::map<std::string, std::string>* CcdbApi::retrieveMetaInfo(TFile& file)
1582{
1583 auto object = file.GetObjectChecked(CCDBMETA_ENTRY, TClass::GetClass(typeid(std::map<std::string, std::string>)));
1584 if (object) {
1585 return static_cast<std::map<std::string, std::string>*>(object);
1586 }
1587 return nullptr;
1588}
1589
1590namespace
1591{
1592void traverseAndFillFolders(CcdbApi const& api, std::string const& top, std::vector<std::string>& folders)
1593{
1594 // LOG(info) << "Querying " << top;
1595 auto reply = api.list(top);
1596 folders.emplace_back(top);
1597 // LOG(info) << reply;
1598 auto subfolders = api.parseSubFolders(reply);
1599 if (subfolders.size() > 0) {
1600 // LOG(info) << subfolders.size() << " folders in " << top;
1601 for (auto& sub : subfolders) {
1602 traverseAndFillFolders(api, sub, folders);
1603 }
1604 } else {
1605 // LOG(info) << "NO subfolders in " << top;
1606 }
1607}
1608} // namespace
1609
1610std::vector<std::string> CcdbApi::getAllFolders(std::string const& top) const
1611{
1612 std::vector<std::string> folders;
1613 traverseAndFillFolders(*this, top, folders);
1614 return folders;
1615}
1616
1617TClass* CcdbApi::tinfo2TClass(std::type_info const& tinfo)
1618{
1619 TClass* cl = TClass::GetClass(tinfo);
1620 if (!cl) {
1621 throw std::runtime_error(fmt::format("Could not retrieve ROOT dictionary for type {}, aborting", tinfo.name()));
1622 return nullptr;
1623 }
1624 return cl;
1625}
1626
1627int CcdbApi::updateMetadata(std::string const& path, std::map<std::string, std::string> const& metadata, long timestamp, std::string const& id, long newEOV)
1628{
1629 int ret = -1;
1630 CURL* curl = curl_easy_init();
1631 curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str());
1632 if (curl != nullptr) {
1633 CURLcode res;
1634 stringstream fullUrl;
1635 for (size_t hostIndex = 0; hostIndex < hostsPool.size(); hostIndex++) {
1636 fullUrl << getHostUrl(hostIndex) << "/" << path << "/" << timestamp;
1637 if (newEOV > 0) {
1638 fullUrl << "/" << newEOV;
1639 }
1640 if (!id.empty()) {
1641 fullUrl << "/" << id;
1642 }
1643 fullUrl << "?";
1644
1645 for (auto& kv : metadata) {
1646 std::string mfirst = kv.first;
1647 std::string msecond = kv.second;
1648 // same trick for the metadata as for the object type
1649 char* mfirstEncoded = curl_easy_escape(curl, mfirst.c_str(), mfirst.size());
1650 char* msecondEncoded = curl_easy_escape(curl, msecond.c_str(), msecond.size());
1651 fullUrl << std::string(mfirstEncoded) + "=" + std::string(msecondEncoded) + "&";
1652 curl_free(mfirstEncoded);
1653 curl_free(msecondEncoded);
1654 }
1655
1656 if (curl != nullptr) {
1657 LOG(debug) << "passing to curl: " << fullUrl.str();
1658 curl_easy_setopt(curl, CURLOPT_URL, fullUrl.str().c_str());
1659 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); // make sure we use PUT
1660 curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str());
1661 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
1662 curlSetSSLOptions(curl);
1663
1664 // Perform the request, res will get the return code
1665 res = CURL_perform(curl);
1666 if (res != CURLE_OK) {
1667 LOGP(alarm, "CURL_perform() failed: {}, code: {}", curl_easy_strerror(res), int(res));
1668 ret = int(res);
1669 } else {
1670 ret = 0;
1671 }
1672 curl_easy_cleanup(curl);
1673 }
1674 }
1675 }
1676 return ret;
1677}
1678
1679void CcdbApi::initHostsPool(std::string hosts)
1680{
1681 hostsPool.clear();
1682 auto splitted = hosts | std::views::transform([](char c) { return (c == ';') ? ',' : c; }) | std::views::split(',');
1683 for (auto&& part : splitted) {
1684 hostsPool.emplace_back(part.begin(), part.end());
1685 }
1686}
1687
1688std::string CcdbApi::getHostUrl(int hostIndex) const
1689{
1690 return hostsPool.at(hostIndex);
1691}
1692
1693void CcdbApi::scheduleDownload(RequestContext& requestContext, size_t* requestCounter) const
1694{
1695 auto data = new DownloaderRequestData(); // Deleted in transferFinished of CCDBDownloader.cxx
1696 data->hoPair.object = &requestContext.dest;
1697
1698 std::function<bool(std::string)> localContentCallback = [this, &requestContext](std::string url) {
1699 return this->loadLocalContentToMemory(requestContext.dest, url);
1700 };
1701
1702 auto writeCallback = [](void* contents, size_t size, size_t nmemb, void* chunkptr) {
1703 auto& ho = *static_cast<HeaderObjectPair_t*>(chunkptr);
1704 auto& chunk = *ho.object;
1705 size_t realsize = size * nmemb, sz = 0;
1706 ho.counter++;
1707 try {
1708 if (chunk.capacity() < chunk.size() + realsize) {
1709 // estimate headers size when converted to annotated text string
1710 const char hannot[] = "header";
1711 size_t hsize = getFlatHeaderSize(ho.header);
1712 auto cl = ho.header.find("Content-Length");
1713 if (cl != ho.header.end()) {
1714 size_t sizeFromHeader = std::stol(cl->second);
1715 sz = hsize + std::max(chunk.size() * (sizeFromHeader ? 1 : 2) + realsize, sizeFromHeader);
1716 } else {
1717 sz = hsize + std::max(chunk.size() * 2, chunk.size() + realsize);
1718 // LOGP(debug, "SIZE IS NOT IN HEADER, allocate {}", sz);
1719 }
1720 chunk.reserve(sz);
1721 }
1722 char* contC = (char*)contents;
1723 chunk.insert(chunk.end(), contC, contC + realsize);
1724 } catch (std::exception e) {
1725 // LOGP(alarm, "failed to reserve {} bytes in CURL write callback (realsize = {}): {}", sz, realsize, e.what());
1726 realsize = 0;
1727 }
1728 return realsize;
1729 };
1730
1731 CURL* curl_handle = curl_easy_init();
1732 curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, mUniqueAgentID.c_str());
1733 std::string fullUrl = getFullUrlForRetrieval(curl_handle, requestContext.path, requestContext.metadata, requestContext.timestamp);
1734 curl_slist* options_list = nullptr;
1735 initCurlHTTPHeaderOptionsForRetrieve(curl_handle, options_list, requestContext.timestamp, &requestContext.headers,
1736 requestContext.etag, requestContext.createdNotAfter, requestContext.createdNotBefore);
1737
1738 data->headers = &requestContext.headers;
1739 data->hosts = hostsPool;
1740 data->path = requestContext.path;
1741 data->timestamp = requestContext.timestamp;
1742 data->localContentCallback = localContentCallback;
1743 data->userAgent = mUniqueAgentID;
1744 data->optionsList = options_list;
1745
1746 curl_easy_setopt(curl_handle, CURLOPT_URL, fullUrl.c_str());
1747 initCurlOptionsForRetrieve(curl_handle, (void*)(&data->hoPair), writeCallback, false);
1748 curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_map_callback<decltype(data->hoPair.header)>);
1749 curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void*)&(data->hoPair.header));
1750 curl_easy_setopt(curl_handle, CURLOPT_PRIVATE, (void*)data);
1751 curlSetSSLOptions(curl_handle);
1752
1753 asynchPerform(curl_handle, requestCounter);
1754}
1755
1756std::string CcdbApi::determineSemaphoreName(std::string const& basedir, std::string const& ccdbpath)
1757{
1758 std::hash<std::string> hasher;
1759 std::string semhashedstring = "aliceccdb" + std::to_string(hasher(basedir + ccdbpath)).substr(0, 16);
1760 return semhashedstring;
1761}
1762
1763boost::interprocess::named_semaphore* CcdbApi::createNamedSemaphore(std::string const& path) const
1764{
1765 std::string semhashedstring = determineSemaphoreName(mSnapshotCachePath, path);
1766 // LOG(info) << "Creating named semaphore with name " << semhashedstring.c_str();
1767 try {
1768 return new boost::interprocess::named_semaphore(boost::interprocess::open_or_create_t{}, semhashedstring.c_str(), 1);
1769 } catch (std::exception e) {
1770 LOG(warn) << "Exception occurred during CCDB (cache) semaphore setup; Continuing without";
1771 return nullptr;
1772 }
1773}
1774
1775void CcdbApi::releaseNamedSemaphore(boost::interprocess::named_semaphore* sem, std::string const& path) const
1776{
1777 if (sem) {
1778 sem->post();
1779 if (sem->try_wait()) { // if nobody else is waiting remove the semaphore resource
1780 sem->post();
1781 boost::interprocess::named_semaphore::remove(determineSemaphoreName(mSnapshotCachePath, path).c_str());
1782 }
1783 }
1784}
1785
1786bool CcdbApi::removeSemaphore(std::string const& semaname, bool remove)
1787{
1788 // removes a given named semaphore from the system
1789 try {
1790 boost::interprocess::named_semaphore semaphore(boost::interprocess::open_only, semaname.c_str());
1791 std::cout << "Found CCDB semaphore: " << semaname << "\n";
1792 if (remove) {
1793 auto success = boost::interprocess::named_semaphore::remove(semaname.c_str());
1794 if (success) {
1795 std::cout << "Removed CCDB semaphore: " << semaname << "\n";
1796 }
1797 return success;
1798 }
1799 return true;
1800 } catch (std::exception const& e) {
1801 // no EXISTING under this name semaphore found
1802 // nothing to be done
1803 }
1804 return false;
1805}
1806
1807// helper function checking for leaking semaphores associated to CCDB cache files and removing them
1808// walks a local CCDB snapshot tree and checks
1809void CcdbApi::removeLeakingSemaphores(std::string const& snapshotdir, bool remove)
1810{
1811 namespace fs = std::filesystem;
1812 std::string fileName{"snapshot.root"};
1813 try {
1814 auto absolutesnapshotdir = fs::weakly_canonical(fs::absolute(snapshotdir));
1815 for (const auto& entry : fs::recursive_directory_iterator(absolutesnapshotdir)) {
1816 if (entry.is_directory()) {
1817 const fs::path& currentDir = fs::canonical(fs::absolute(entry.path()));
1818 fs::path filePath = currentDir / fileName;
1819 if (fs::exists(filePath) && fs::is_regular_file(filePath)) {
1820 std::cout << "Directory with file '" << fileName << "': " << currentDir << std::endl;
1821
1822 // we need to obtain the path relative to snapshotdir
1823 auto pathtokens = o2::utils::Str::tokenize(currentDir, '/', true);
1824 auto numtokens = pathtokens.size();
1825 if (numtokens < 3) {
1826 // cannot be a CCDB path
1827 continue;
1828 }
1829 // path are last 3 entries
1830 std::string path = pathtokens[numtokens - 3] + "/" + pathtokens[numtokens - 2] + "/" + pathtokens[numtokens - 1];
1831 auto semaname = o2::ccdb::CcdbApi::determineSemaphoreName(absolutesnapshotdir, path);
1832 removeSemaphore(semaname, remove);
1833 }
1834 }
1835 }
1836 } catch (std::exception const& e) {
1837 LOG(info) << "Semaphore search had exception " << e.what();
1838 }
1839}
1840
1841void CcdbApi::getFromSnapshot(bool createSnapshot, std::string const& path,
1842 long timestamp, std::map<std::string, std::string>& headers,
1843 std::string& snapshotpath, o2::pmr::vector<char>& dest, int& fromSnapshot, std::string const& etag) const
1844{
1845 if (createSnapshot) { // create named semaphore
1846 std::string logfile = mSnapshotCachePath + "/log";
1847 std::fstream logStream = std::fstream(logfile, ios_base::out | ios_base::app);
1848 if (logStream.is_open()) {
1849 logStream << "CCDB-access[" << getpid() << "] of " << mUniqueAgentID << " to " << path << " timestamp " << timestamp << " for load to memory\n";
1850 }
1851 }
1852 if (mInSnapshotMode) { // file must be there, otherwise a fatal will be produced;
1853 if (etag.empty()) {
1854 loadFileToMemory(dest, getSnapshotFile(mSnapshotTopPath, path), &headers);
1855 }
1856 fromSnapshot = 1;
1857 } else if (mPreferSnapshotCache && std::filesystem::exists(snapshotpath)) {
1858 // if file is available, use it, otherwise cache it below from the server. Do this only when etag is empty since otherwise the object was already fetched and cached
1859 if (etag.empty()) {
1860 loadFileToMemory(dest, snapshotpath, &headers);
1861 }
1862 fromSnapshot = 2;
1863 }
1864}
1865
1866void CcdbApi::saveSnapshot(RequestContext& requestContext) const
1867{
1868 // Consider saving snapshot
1869 if (!mSnapshotCachePath.empty() && !(mInSnapshotMode && mSnapshotTopPath == mSnapshotCachePath)) { // store in the snapshot only if the object was not read from the snapshot
1870 auto semaphore_barrier = std::make_unique<CCDBSemaphore>(mSnapshotCachePath, requestContext.path);
1871
1872 auto snapshotdir = getSnapshotDir(mSnapshotCachePath, requestContext.path);
1873 std::string snapshotpath = getSnapshotFile(mSnapshotCachePath, requestContext.path);
1875 std::fstream logStream;
1876 if (logStream.is_open()) {
1877 logStream << "CCDB-access[" << getpid() << "] ... " << mUniqueAgentID << " downloading to snapshot " << snapshotpath << " from memory\n";
1878 }
1879 { // dump image to a file
1880 LOGP(debug, "creating snapshot {} -> {}", requestContext.path, snapshotpath);
1881 CCDBQuery querysummary(requestContext.path, requestContext.metadata, requestContext.timestamp);
1882 {
1883 std::ofstream objFile(snapshotpath, std::ios::out | std::ofstream::binary);
1884 std::copy(requestContext.dest.begin(), requestContext.dest.end(), std::ostreambuf_iterator<char>(objFile));
1885 }
1886 // now open the same file as root file and store metadata
1887 updateMetaInformationInLocalFile(snapshotpath, &requestContext.headers, &querysummary);
1888 }
1889 }
1890}
1891
1892void CcdbApi::loadFileToMemory(std::vector<char>& dest, std::string const& path,
1893 std::map<std::string, std::string> const& metadata, long timestamp,
1894 std::map<std::string, std::string>* headers, std::string const& etag,
1895 const std::string& createdNotAfter, const std::string& createdNotBefore, bool considerSnapshot) const
1896{
1898 destP.reserve(dest.size());
1899 loadFileToMemory(destP, path, metadata, timestamp, headers, etag, createdNotAfter, createdNotBefore, considerSnapshot);
1900 dest.clear();
1901 dest.reserve(destP.size());
1902 for (const auto c : destP) {
1903 dest.push_back(c);
1904 }
1905}
1906
1908 std::map<std::string, std::string> const& metadata, long timestamp,
1909 std::map<std::string, std::string>* headers, std::string const& etag,
1910 const std::string& createdNotAfter, const std::string& createdNotBefore, bool considerSnapshot) const
1911{
1912 RequestContext requestContext(dest, metadata, *headers);
1913 requestContext.path = path;
1914 // std::map<std::string, std::string> metadataCopy = metadata; // Create a copy because metadata will be passed as a pointer so it cannot be constant. The const in definition is for backwards compatability.
1915 // requestContext.metadata = metadataCopy;
1916 requestContext.timestamp = timestamp;
1917 requestContext.etag = etag;
1918 requestContext.createdNotAfter = createdNotAfter;
1919 requestContext.createdNotBefore = createdNotBefore;
1920 requestContext.considerSnapshot = considerSnapshot;
1921 std::vector<RequestContext> contexts = {requestContext};
1922 vectoredLoadFileToMemory(contexts);
1923}
1924
1925void CcdbApi::appendFlatHeader(o2::pmr::vector<char>& dest, const std::map<std::string, std::string>& headers)
1926{
1927 size_t hsize = getFlatHeaderSize(headers), cnt = dest.size();
1928 dest.resize(cnt + hsize);
1929 auto addString = [&dest, &cnt](const std::string& s) {
1930 for (char c : s) {
1931 dest[cnt++] = c;
1932 }
1933 dest[cnt++] = 0;
1934 };
1935
1936 for (auto& h : headers) {
1937 addString(h.first);
1938 addString(h.second);
1939 }
1940 *reinterpret_cast<int*>(&dest[cnt]) = hsize; // store size
1941 std::memcpy(&dest[cnt + sizeof(int)], FlatHeaderAnnot, sizeof(FlatHeaderAnnot)); // annotate the flattened headers map
1942}
1943
1944void CcdbApi::navigateSourcesAndLoadFile(RequestContext& requestContext, int& fromSnapshot, size_t* requestCounter) const
1945{
1946 LOGP(debug, "loadFileToMemory {} ETag=[{}]", requestContext.path, requestContext.etag);
1947 bool createSnapshot = requestContext.considerSnapshot && !mSnapshotCachePath.empty(); // create snaphot if absent
1948
1949 std::string snapshotpath;
1950 if (mInSnapshotMode || std::filesystem::exists(snapshotpath = getSnapshotFile(mSnapshotCachePath, requestContext.path))) {
1951 auto semaphore_barrier = std::make_unique<CCDBSemaphore>(mSnapshotCachePath, requestContext.path);
1952 // if we are in snapshot mode we can simply open the file, unless the etag is non-empty:
1953 // this would mean that the object was is already fetched and in this mode we don't to validity checks!
1954 getFromSnapshot(createSnapshot, requestContext.path, requestContext.timestamp, requestContext.headers, snapshotpath, requestContext.dest, fromSnapshot, requestContext.etag);
1955 } else { // look on the server
1956 scheduleDownload(requestContext, requestCounter);
1957 }
1958}
1959
1960void CcdbApi::vectoredLoadFileToMemory(std::vector<RequestContext>& requestContexts) const
1961{
1962 std::vector<int> fromSnapshots(requestContexts.size());
1963 size_t requestCounter = 0;
1964
1965 // Get files from snapshots and schedule downloads
1966 for (int i = 0; i < requestContexts.size(); i++) {
1967 // navigateSourcesAndLoadFile either retrieves file from snapshot immediately, or schedules it to be downloaded when mDownloader->runLoop is ran at a later time
1968 auto& requestContext = requestContexts.at(i);
1969 navigateSourcesAndLoadFile(requestContext, fromSnapshots.at(i), &requestCounter);
1970 }
1971
1972 // Download the rest
1973 while (requestCounter > 0) {
1974 mDownloader->runLoop(0);
1975 }
1976
1977 // Save snapshots
1978 for (int i = 0; i < requestContexts.size(); i++) {
1979 auto& requestContext = requestContexts.at(i);
1980 if (!requestContext.dest.empty()) {
1981 logReading(requestContext.path, requestContext.timestamp, &requestContext.headers,
1982 fmt::format("{}{}", requestContext.considerSnapshot ? "load to memory" : "retrieve", fromSnapshots.at(i) ? " from snapshot" : ""));
1983 if (requestContext.considerSnapshot && fromSnapshots.at(i) != 2) {
1984 saveSnapshot(requestContext);
1985 }
1986 }
1987 }
1988}
1989
1991{
1992 if (url.find("alien:/", 0) != std::string::npos) {
1993 std::map<std::string, std::string> localHeaders;
1994 loadFileToMemory(dest, url, &localHeaders, false);
1995 auto it = localHeaders.find("Error");
1996 if (it != localHeaders.end() && it->second == "An error occurred during retrieval") {
1997 return false;
1998 } else {
1999 return true;
2000 }
2001 }
2002 if ((url.find("file:/", 0) != std::string::npos)) {
2003 std::string path = url.substr(7);
2004 if (std::filesystem::exists(path)) {
2005 std::map<std::string, std::string> localHeaders;
2006 loadFileToMemory(dest, url, &localHeaders, o2::utils::Str::endsWith(path, ".root"));
2007 auto it = localHeaders.find("Error");
2008 if (it != localHeaders.end() && it->second == "An error occurred during retrieval") {
2009 return false;
2010 } else {
2011 return true;
2012 }
2013 }
2014 }
2015 return false;
2016}
2017
2018void CcdbApi::loadFileToMemory(o2::pmr::vector<char>& dest, const std::string& path, std::map<std::string, std::string>* localHeaders, bool fetchLocalMetaData) const
2019{
2020 // Read file to memory as vector. For special case of the locally cached file retriev metadata stored directly in the file
2021 constexpr size_t MaxCopySize = 0x1L << 25;
2022 auto signalError = [&dest, localHeaders]() {
2023 dest.clear();
2024 dest.reserve(1);
2025 if (localHeaders) { // indicate that an error occurred ---> used by caching layers (such as CCDBManager)
2026 (*localHeaders)["Error"] = "An error occurred during retrieval";
2027 }
2028 };
2029 if (path.find("alien:/") == 0 && !initTGrid()) {
2030 signalError();
2031 return;
2032 }
2033 std::string fname(path);
2034 if (fname.find("?filetype=raw") == std::string::npos) {
2035 fname += "?filetype=raw";
2036 }
2037 std::unique_ptr<TFile> sfile{TFile::Open(fname.c_str())};
2038 if (!sfile || sfile->IsZombie()) {
2039 LOG(error) << "Failed to open file " << fname;
2040 signalError();
2041 return;
2042 }
2043 size_t totalread = 0, fsize = sfile->GetSize(), b00 = sfile->GetBytesRead();
2044 dest.resize(fsize);
2045 char* dptr = dest.data();
2046 sfile->Seek(0);
2047 long nread = 0;
2048 do {
2049 size_t b0 = sfile->GetBytesRead(), b1 = b0 - b00;
2050 size_t readsize = fsize - b1 > MaxCopySize ? MaxCopySize : fsize - b1;
2051 if (readsize == 0) {
2052 break;
2053 }
2054 sfile->Seek(totalread, TFile::kBeg);
2055 bool failed = sfile->ReadBuffer(dptr, (Int_t)readsize);
2056 nread = sfile->GetBytesRead() - b0;
2057 if (failed || nread < 0) {
2058 LOG(error) << "failed to copy file " << fname << " to memory buffer";
2059 signalError();
2060 return;
2061 }
2062 dptr += nread;
2063 totalread += nread;
2064 } while (nread == (long)MaxCopySize);
2065
2066 if (localHeaders && fetchLocalMetaData) {
2067 TMemFile memFile("name", const_cast<char*>(dest.data()), dest.size(), "READ");
2068 auto storedmeta = (std::map<std::string, std::string>*)extractFromTFile(memFile, TClass::GetClass("std::map<std::string, std::string>"), CCDBMETA_ENTRY);
2069 if (storedmeta) {
2070 *localHeaders = *storedmeta; // do a simple deep copy
2071 delete storedmeta;
2072 }
2073 if ((isSnapshotMode() || mPreferSnapshotCache) && localHeaders->find("ETag") == localHeaders->end()) { // generate dummy ETag to profit from the caching
2074 (*localHeaders)["ETag"] = path;
2075 }
2076 if (localHeaders->find("fileSize") == localHeaders->end()) {
2077 (*localHeaders)["fileSize"] = fmt::format("{}", memFile.GetEND());
2078 }
2079 }
2080 return;
2081}
2082
2083void CcdbApi::checkMetadataKeys(std::map<std::string, std::string> const& metadata) const
2084{
2085
2086 // function to check if any key contains invalid characters
2087 // if so, a fatal will be issued
2088
2089 const std::regex regexPatternSearch(R"([ :;.,\\/'?!\‍(\)\{\}\[\]@<>=+*#$&`|~^%])");
2090 bool isInvalid = false;
2091
2092 for (auto& el : metadata) {
2093 auto keyMd = el.first;
2094 auto tmp = keyMd;
2095 std::smatch searchRes;
2096 while (std::regex_search(keyMd, searchRes, regexPatternSearch)) {
2097 isInvalid = true;
2098 LOG(error) << "Invalid character found in metadata key '" << tmp << "\': '" << searchRes.str() << "\'";
2099 keyMd = searchRes.suffix();
2100 }
2101 }
2102 if (isInvalid) {
2103 LOG(fatal) << "Some metadata keys have invalid characters, please fix!";
2104 }
2105 return;
2106}
2107
2108void CcdbApi::logReading(const std::string& path, long ts, const std::map<std::string, std::string>* headers, const std::string& comment) const
2109{
2110 std::string upath{path};
2111 if (headers) {
2112 auto ent = headers->find("Valid-From");
2113 if (ent != headers->end()) {
2114 upath += "/" + ent->second;
2115 }
2116 ent = headers->find("ETag");
2117 if (ent != headers->end()) {
2118 upath += "/" + ent->second;
2119 }
2120 }
2121 upath.erase(remove(upath.begin(), upath.end(), '\"'), upath.end());
2122 LOGP(info, "ccdb reads {}{}{} for {} ({}, agent_id: {}), ", mUrl, mUrl.back() == '/' ? "" : "/", upath, ts < 0 ? getCurrentTimestamp() : ts, comment, mUniqueAgentID);
2123}
2124
2125void CcdbApi::asynchPerform(CURL* handle, size_t* requestCounter) const
2126{
2127 mDownloader->asynchSchedule(handle, requestCounter);
2128}
2129
2130CURLcode CcdbApi::CURL_perform(CURL* handle) const
2131{
2132 if (mIsCCDBDownloaderPreferred) {
2133 return mDownloader->perform(handle);
2134 }
2135 CURLcode result;
2136 for (int i = 1; i <= mCurlRetries && (result = curl_easy_perform(handle)) != CURLE_OK; i++) {
2137 usleep(mCurlDelayRetries * i);
2138 }
2139 return result;
2140}
2141
2146CCDBSemaphore::CCDBSemaphore(std::string const& snapshotpath, std::string const& path)
2147{
2148 LOG(debug) << "Entering semaphore barrier";
2149 mSemName = CcdbApi::determineSemaphoreName(snapshotpath, path);
2150 try {
2151 mSem = new boost::interprocess::named_semaphore(boost::interprocess::open_or_create_t{}, mSemName.c_str(), 1);
2152 } catch (std::exception e) {
2153 LOG(warn) << "Exception occurred during CCDB (cache) semaphore setup; Continuing without";
2154 mSem = nullptr;
2155 }
2156 // automatically wait
2157 if (mSem) {
2158 gSemaRegistry.add(this);
2159 mSem->wait();
2160 }
2161}
2162
2164{
2165 LOG(debug) << "Ending semaphore barrier";
2166 if (mSem) {
2167 mSem->post();
2168 if (mSem->try_wait()) { // if nobody else is waiting remove the semaphore resource
2169 mSem->post();
2170 boost::interprocess::named_semaphore::remove(mSemName.c_str());
2171 }
2172 gSemaRegistry.remove(this);
2173 }
2174}
2175
2177{
2178 LOG(debug) << "Cleaning up semaphore registry with count " << mStore.size();
2179 for (auto& s : mStore) {
2180 delete s;
2181 mStore.erase(s);
2182 }
2183}
2184
2186{
2187 mStore.insert(ptr);
2188}
2189
2191{
2192 mStore.erase(ptr);
2193}
2194
2195} // namespace o2::ccdb
std::string createdNotBefore
std::string createdNotAfter
size_t maxSize
std::string etag
std::string url
std::ostringstream debug
int32_t i
#define failed(...)
Definition Utils.h:42
uint8_t errorflag
Definition RawData.h:0
uint32_t res
Definition RawData.h:0
uint32_t c
Definition RawData.h:2
TBranch * ptr
StringRef key
Class for time synchronization of RawReader instances.
void setRequestTimeoutTime(int timeoutMS)
CURLcode perform(CURL *handle)
void asynchSchedule(CURL *handle, size_t *requestCounter)
void setKeepaliveTimeoutTime(int timeoutMS)
CCDBSemaphore(std::string const &cachepath, std::string const &path)
Definition CcdbApi.cxx:2146
static void curlSetSSLOptions(CURL *curl)
Definition CcdbApi.cxx:620
static std::string generateFileName(const std::string &inp)
Definition CcdbApi.cxx:823
std::string list(std::string const &path="", bool latestOnly=false, std::string const &returnFormat="text/plain", long createdNotAfter=-1, long createdNotBefore=-1) const
Definition CcdbApi.cxx:1219
int storeAsTFile_impl(const void *obj1, std::type_info const &info, std::string const &path, std::map< std::string, std::string > const &metadata, long startValidityTimestamp=-1, long endValidityTimestamp=-1, std::vector< char >::size_type maxSize=0) const
Definition CcdbApi.cxx:333
static bool checkAlienToken()
Definition CcdbApi.cxx:123
void runDownloaderLoop(bool noWait)
Definition CcdbApi.cxx:265
void releaseNamedSemaphore(boost::interprocess::named_semaphore *sem, std::string const &path) const
Definition CcdbApi.cxx:1775
static std::map< std::string, std::string > * retrieveMetaInfo(TFile &)
Definition CcdbApi.cxx:1581
void scheduleDownload(RequestContext &requestContext, size_t *requestCounter) const
Definition CcdbApi.cxx:1693
TObject * retrieve(std::string const &path, std::map< std::string, std::string > const &metadata, long timestamp) const
Definition CcdbApi.cxx:795
void init(std::string const &hosts)
Definition CcdbApi.cxx:160
TObject * retrieveFromTFile(std::string const &path, std::map< std::string, std::string > const &metadata, long timestamp, std::map< std::string, std::string > *headers, std::string const &etag, const std::string &createdNotAfter, const std::string &createdNotBefore) const
Definition CcdbApi.cxx:833
std::string const & getURL() const
Definition CcdbApi.h:87
void getFromSnapshot(bool createSnapshot, std::string const &path, long timestamp, std::map< std::string, std::string > &headers, std::string &snapshotpath, o2::pmr::vector< char > &dest, int &fromSnapshot, std::string const &etag) const
Definition CcdbApi.cxx:1841
bool loadLocalContentToMemory(o2::pmr::vector< char > &dest, std::string &url) const
Definition CcdbApi.cxx:1990
static void removeLeakingSemaphores(std::string const &basedir, bool remove=false)
Definition CcdbApi.cxx:1809
void saveSnapshot(RequestContext &requestContext) const
Definition CcdbApi.cxx:1866
static std::unique_ptr< std::vector< char > > createObjectImage(const T *obj, CcdbObjectInfo *info=nullptr)
Definition CcdbApi.h:103
static void * extractFromTFile(TFile &file, TClass const *cl, const char *what=CCDBOBJECT_ENTRY)
Definition CcdbApi.cxx:905
int storeAsTFile(const TObject *rootObject, std::string const &path, std::map< std::string, std::string > const &metadata, long startValidityTimestamp=-1, long endValidityTimestamp=-1, std::vector< char >::size_type maxSize=0) const
Definition CcdbApi.cxx:474
void snapshot(std::string const &ccdbrootpath, std::string const &localDir, long timestamp) const
Definition CcdbApi.cxx:895
bool isSnapshotMode() const
Definition CcdbApi.h:93
bool isHostReachable() const
Definition CcdbApi.cxx:1329
void loadFileToMemory(std::vector< char > &dest, std::string const &path, std::map< std::string, std::string > const &metadata, long timestamp, std::map< std::string, std::string > *headers, std::string const &etag, const std::string &createdNotAfter, const std::string &createdNotBefore, bool considerSnapshot=true) const
Definition CcdbApi.cxx:1892
static std::string determineSemaphoreName(std::string const &basedir, std::string const &objectpath)
Definition CcdbApi.cxx:1756
void deleteObject(std::string const &path, long timestamp=-1) const
Definition CcdbApi.cxx:1271
static void appendFlatHeader(o2::pmr::vector< char > &dest, const std::map< std::string, std::string > &headers)
Definition CcdbApi.cxx:1925
std::vector< std::string > getAllFolders(std::string const &top) const
Definition CcdbApi.cxx:1610
void vectoredLoadFileToMemory(std::vector< RequestContext > &requestContext) const
Definition CcdbApi.cxx:1960
boost::interprocess::named_semaphore * createNamedSemaphore(std::string const &path) const
Definition CcdbApi.cxx:1763
static bool removeSemaphore(std::string const &name, bool remove=false)
Definition CcdbApi.cxx:1786
std::map< std::string, std::string > retrieveHeaders(std::string const &path, std::map< std::string, std::string > const &metadata, long timestamp=-1) const
Definition CcdbApi.cxx:1444
CcdbApi()
Default constructor.
Definition CcdbApi.cxx:96
static bool getCCDBEntryHeaders(std::string const &url, std::string const &etag, std::vector< std::string > &headers, const std::string &agentID="")
Definition CcdbApi.cxx:1524
static CCDBQuery * retrieveQueryInfo(TFile &)
Definition CcdbApi.cxx:1572
static constexpr const char * CCDBQUERY_ENTRY
Definition CcdbApi.h:335
void truncate(std::string const &path) const
Definition CcdbApi.cxx:1298
int storeAsBinaryFile(const char *buffer, size_t size, const std::string &fileName, const std::string &objectType, const std::string &path, const std::map< std::string, std::string > &metadata, long startValidityTimestamp, long endValidityTimestamp, std::vector< char >::size_type maxSize=0) const
Definition CcdbApi.cxx:349
static void parseCCDBHeaders(std::vector< std::string > const &headers, std::vector< std::string > &pfns, std::string &etag)
Definition CcdbApi.cxx:1559
bool retrieveBlob(std::string const &path, std::string const &targetdir, std::map< std::string, std::string > const &metadata, long timestamp, bool preservePathStructure=true, std::string const &localFileName="snapshot.root", std::string const &createdNotAfter="", std::string const &createdNotBefore="", std::map< std::string, std::string > *headers=nullptr) const
Definition CcdbApi.cxx:840
int updateMetadata(std::string const &path, std::map< std::string, std::string > const &metadata, long timestamp, std::string const &id="", long newEOV=0)
Definition CcdbApi.cxx:1627
static constexpr const char * CCDBMETA_ENTRY
Definition CcdbApi.h:336
static constexpr const char * CCDBOBJECT_ENTRY
Definition CcdbApi.h:337
void navigateSourcesAndLoadFile(RequestContext &requestContext, int &fromSnapshot, size_t *requestCounter) const
Definition CcdbApi.cxx:1944
std::vector< std::string > parseSubFolders(std::string const &reply) const
Definition CcdbApi.cxx:1352
virtual ~CcdbApi()
Default destructor.
Definition CcdbApi.cxx:112
void add(CCDBSemaphore const *ptr)
Definition CcdbApi.cxx:2185
void remove(CCDBSemaphore const *ptr)
Definition CcdbApi.cxx:2190
const GLfloat * m
Definition glcorearb.h:4066
GLuint64EXT * result
Definition glcorearb.h:5662
GLint void * img
Definition glcorearb.h:550
GLuint buffer
Definition glcorearb.h:655
GLuint entry
Definition glcorearb.h:5735
GLsizeiptr size
Definition glcorearb.h:659
GLuint GLuint end
Definition glcorearb.h:469
GLuint index
Definition glcorearb.h:781
GLdouble GLdouble GLdouble GLdouble top
Definition glcorearb.h:4077
GLdouble f
Definition glcorearb.h:310
GLenum GLint * range
Definition glcorearb.h:1899
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLboolean * data
Definition glcorearb.h:298
GLsizei const GLchar *const * path
Definition glcorearb.h:3591
GLuint object
Definition glcorearb.h:4041
GLuint start
Definition glcorearb.h:469
GLuint GLuint stream
Definition glcorearb.h:1806
GLbitfield GLuint64 timeout
Definition glcorearb.h:1573
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition glcorearb.h:2514
GLuint id
Definition glcorearb.h:650
information complementary to a CCDB object (path, metadata, startTimeValidity, endTimeValidity etc)
bool stdmap_to_jsonfile(std::map< std::string, std::string > const &meta, std::string const &filename)
Definition CcdbApi.cxx:1388
size_t write_data(void *, size_t size, size_t nmemb, void *)
Definition CcdbApi.cxx:1324
long getCurrentTimestamp()
returns the timestamp in long corresponding to "now"
size_t(*)(void *, size_t, size_t, void *) CurlWriteCallback
Definition CcdbApi.cxx:646
std::string sanitizeObjectName(const std::string &objectName)
Definition CcdbApi.cxx:296
std::mutex gIOMutex
Definition CcdbApi.cxx:60
bool jsonfile_to_stdmap(std::map< std::string, std::string > &meta, std::string const &filename)
Definition CcdbApi.cxx:1417
long getFutureTimestamp(int secondsInFuture)
returns the timestamp in long corresponding to "now + secondsInFuture"
size_t CurlWrite_CallbackFunc_StdString2(void *contents, size_t size, size_t nmemb, std::string *s)
Definition CcdbApi.cxx:1204
std::string timestamp() noexcept
Definition Clock.h:84
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
std::vector< T, fair::mq::pmr::polymorphic_allocator< T > > vector
void createDirectoriesIfAbsent(std::string const &path)
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
std::string filename()
void empty(int)
std::map< std::string, std::string > const & metadata
Definition CcdbApi.h:360
std::map< std::string, std::string > & headers
Definition CcdbApi.h:362
o2::pmr::vector< char > & dest
Definition CcdbApi.h:358
o2::pmr::vector< char > * object
static DeploymentMode deploymentMode()
static std::string getClassName(const T &obj)
get the class name of the object
static std::unique_ptr< FileImage > createFileImage(const TObject &obj, const std::string &fileName, const std::string &objName)
static void trim(std::string &s)
Definition StringUtils.h:70
static std::vector< std::string > tokenize(const std::string &src, char delim, bool trimToken=true, bool skipEmpty=true)
static std::string concat_string(Ts const &... ts)
static bool endsWith(const std::string &s, const std::string &ending)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))
const std::string str
uint64_t const void const *restrict const msg
Definition x9.h:153