9#include <framework/database/PayloadProvider.h>
10#include <framework/logging/Logger.h>
11#include <framework/utilities/FileSystem.h>
13#include <boost/algorithm/string.hpp>
18namespace fs = std::filesystem;
20namespace Belle2::Conditions {
24 if (!cacheDir.empty()) {
25 m_cacheDir = {fs::absolute(cacheDir).string(),
false};
27 m_cacheDir = {fs::absolute(fs::temp_directory_path() /
"basf2-conditions").string(),
false};
34 for (
auto path : locations) {
35 boost::algorithm::trim(path);
37 B2FATAL(
"Found empty payload location in configuration. "
38 "Please make sure that the conditions database settings are correct");
41 if (
auto pos = path.find(
"://"); pos != std::string::npos) {
43 auto protocol = path.substr(0, pos);
44 boost::algorithm::to_lower(protocol);
45 if (protocol ==
"file") {
46 path = path.substr(pos + 3);
47 }
else if (protocol ==
"http" or protocol ==
"https") {
50 B2ERROR(
"Unknown protocol, only supported protocols for payload download are file, http, https" <<
LogVar(
"protocol", protocol));
55 if (!remote) path = fs::absolute(path).string();
57 B2DEBUG(33,
"Added payload search location" <<
LogVar(remote ?
"url" :
"path", path));
68 return loc.isRemote ? getRemoteFile(loc, metadata) : getLocalFile(loc, metadata);
76 auto fullPath = fs::path(loc.base) /
getFilename(structure, metadata);
78 if (!fs::exists(fullPath))
continue;
80 B2DEBUG(36,
"Checking checksum for payload file" <<
LogVar(
"name", metadata.name) <<
LogVar(
"local dir", loc.base)
81 <<
LogVar(
"revision", metadata.revision) <<
LogVar(
"filename", fullPath) <<
LogVar(
"checksum", metadata.checksum));
83 if (actual == metadata.checksum) {
84 metadata.filename = fullPath.string();
85 B2DEBUG(37,
"Found matching payload file");
88 B2DEBUG(37,
"Checksum doesn't match, continue with next");
98 const bool fallback = loc.base.empty();
99 const auto base = fallback ? metadata.baseUrl : loc.base;
107 auto oldUmask = umask(0);
108 ScopeGuard umaskGuard([oldUmask] {umask(oldUmask);});
109 fs::create_directories(local.parent_path());
110 }
catch (fs::filesystem_error& e) {
111 B2WARNING(
"Cannot create local payload directory" <<
LogVar(
"directory", local.parent_path())
112 <<
LogVar(
"error", e.code().message()));
117 B2DEBUG(37,
"Attempting to lock payload file for writing ..." <<
LogVar(
"filename", local));
122 throw std::runtime_error(
"write lock failed");
126 std::fstream localStream(local.string().c_str(), std::ios::binary | std::ios::in | std::ios::out);
127 B2DEBUG(37,
"Got write lock, check for file access ...");
128 if (!localStream.good()) {
129 B2ERROR(
"Cannot open file for writing" <<
LogVar(
"filename", local) <<
LogVar(
"error", strerror(errno)));
130 throw std::runtime_error(
"cannot open file for writing????");
133 fs::permissions(local, fs::perms::all &
134 ~(fs::perms::owner_exec | fs::perms::group_exec | fs::perms::others_exec));
137 B2DEBUG(37,
"Ok, check digest in case another process downloaded already...");
140 B2DEBUG(37,
"Still not good, download now ...");
144 B2DEBUG(37,
"Payload not found ... trying next source");
149 B2WARNING(
"Conditions Database: checksum mismatch after download. Trying once more in a temporary file"
150 <<
LogVar(
"name", metadata.name) <<
LogVar(
"revision", metadata.revision) <<
LogVar(
"filename", local.string()));
151 throw std::runtime_error(
"checksum mismatch");
153 }
catch (std::exception& e) {
154 B2ERROR(
"Conditions Database: failure downloading url" <<
LogVar(
"url", url) <<
LogVar(
"error", e.what()));
157 B2DEBUG(37,
"Download of payload successful");
160 metadata.filename = local.string();
162 }
catch (std::exception&) {
174 path /= payload.checksum.substr(0, 2);
175 path /= payload.name +
"_r" + std::to_string(payload.revision) +
".root";
178 path /=
"dbstore_" + payload.name +
"_rev_" + std::to_string(payload.revision) +
".root";
181 return path.string();
187 metadata.filename = it->second->getName();
190 const auto openmode = std::ios_base::binary | std::ios_base::in | std::ios_base::out | std::ios_base::trunc;
192 B2DEBUG(37,
"Trying to download into temporary file " << tmpfile->getName());
196 }
catch (std::exception& e) {
197 B2ERROR(
"Conditions Database: failure downloading url" <<
LogVar(
"url", url) <<
LogVar(
"error", e.what()));
200 metadata.filename = tmpfile->getName();
bool download(const std::string &url, std::ostream &stream, bool silentOnMissing=false)
get an url and save the content to stream This function raises exceptions when there are any problems
std::string joinWithSlash(const std::string &base, const std::string &second)
Join two strings and make sure that there is exactly one '/' between them.
bool verifyChecksum(std::istream &input, const std::string &checksum)
check the digest of a stream
PayloadProvider(const std::vector< std::string > &locations, const std::string &cachedir="", int timeout=60)
Constructor for a given list of locations and optionally the location where downloaded payloads shoul...
bool getTemporaryFile(const std::string &url, PayloadMetadata &meta, bool silentOnMissing)
Try to download url into a temporary file, if successful set the filename member of the metadata and ...
PayloadLocation m_cacheDir
Location of the cache directory where/how we want to store downloaded payloads.
bool getLocalFile(const PayloadLocation &loc, PayloadMetadata &meta) const
Look for a payload in the local directory location, set the filename member of the metadata instance ...
std::vector< PayloadLocation > m_locations
List of configured lookup locations: The first one will always be the cache directory and the last on...
std::unordered_map< std::string, std::unique_ptr< FileSystem::TemporaryFile > > m_temporaryFiles
Map of all active temporary files we downloaded and keep around until they can be closed.
int m_timeout
Timeout to wait for a write look when trying to download payloads.
EDirectoryLayout
Enumeration of different directory layouts.
@ c_flat
Flat directory containing the payloads in the form dbstore_{NAME}_rev_{REVISION}.root
@ c_hashed
Hashed directory structure containing the payloads in the form AB/{NAME}_r{REVISION}....
std::string getFilename(EDirectoryLayout structure, const PayloadMetadata &payload) const
Return the filename of a payload to look for given a directory structure and some metadata.
Downloader & m_downloader
Instance to the database file downloading instance.
bool find(PayloadMetadata &meta)
Try to find a payload, return true on success, false if it cannot be found.
bool getRemoteFile(const PayloadLocation &loc, PayloadMetadata &meta)
Look for a payload on a remote server and download if possible, set the filename member of the metada...
Helper class for locking a file.
bool lock(int timeout=300, bool ignoreErrors=false)
Try to lock the file.
Helper file to create a temporary file and ensure deletion if object goes out of scope.
static std::string calculateMD5(const std::string &filename)
Calculate the MD5 checksum of a given file.
Simple ScopeGuard to execute a function at the end of the object lifetime.
Class to store variables with their name which were sent to the logging service.
Simple struct to represent a lookup location.
std::string base
base path or uri