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