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