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