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