File indexing completed on 2025-01-18 10:17:32
0001
0002
0003
0004
0005
0006 #include <JANA/JLogger.h>
0007 #include <JANA/Services/JParameterManager.h>
0008 #include <JANA/Calibrations/JResource.h>
0009
0010 #include <md5.h>
0011
0012 #include <stdlib.h>
0013 #include <unistd.h>
0014 #include <sys/stat.h>
0015 #include <libgen.h>
0016
0017 #include <fstream>
0018
0019 using namespace std;
0020
0021 #ifdef HAVE_CURL
0022 #include <curl/curl.h>
0023 #endif
0024
0025
0026
0027 static pthread_mutex_t resource_manager_mutex = PTHREAD_MUTEX_INITIALIZER;
0028 static string CURRENT_OUTPUT_FNAME = "";
0029
0030 static int mkpath(string s, mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO);
0031
0032 #ifdef HAVE_CURL
0033 static int mycurl_printprogress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow);
0034 #endif
0035
0036
0037
0038
0039
0040 JResource::JResource(std::shared_ptr<JParameterManager> params, JCalibration *jcalib, string resource_dir) {
0041
0042
0043
0044 this->jcalib = jcalib;
0045
0046
0047 jcalib->GetListOfNamepaths(calib_namepaths);
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062 string user = (getenv("USER") == NULL ? "jana" : getenv("USER"));
0063 this->resource_dir = "/tmp/" + user + "/resources";
0064
0065
0066 string RESOURCE_DEFAULT_PATH = "";
0067 if (params != nullptr) {
0068
0069 string description = "Colon (:) separated list of potential directories to be used as the resource directory. Only used if not specified explicitly via JANA:RESOURCE_DIR config. param or JANA_RESOURCE_DIR envar. First directory found to exist is used.";
0070 JParameter *p = NULL;
0071
0072 if (params->Exists("JANA:RESOURCE_DEFAULT_PATH")) {
0073 p = params->GetParameter("JANA:RESOURCE_DEFAULT_PATH", RESOURCE_DEFAULT_PATH);
0074 } else {
0075 p = params->SetParameter("JANA:RESOURCE_DEFAULT_PATH", RESOURCE_DEFAULT_PATH);
0076 }
0077 p->SetDescription(description);
0078 }
0079
0080 if (RESOURCE_DEFAULT_PATH != "") {
0081 stringstream ss(RESOURCE_DEFAULT_PATH);
0082 string path;
0083 while (getline(ss, path, ':')) {
0084 struct stat sb;
0085 if (stat(path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) {
0086 this->resource_dir = path;
0087 break;
0088 }
0089 }
0090 }
0091
0092
0093 const char *JANA_RESOURCE_DIR = getenv("JANA_RESOURCE_DIR");
0094 if (JANA_RESOURCE_DIR) this->resource_dir = JANA_RESOURCE_DIR;
0095
0096
0097 if (params != nullptr) params->SetDefaultParameter("JANA:RESOURCE_DIR", this->resource_dir);
0098
0099
0100 if (resource_dir != "") this->resource_dir = resource_dir;
0101
0102
0103
0104 string local_url = "file://" + this->resource_dir;
0105 jcalibfile = new JCalibrationFile(local_url, 1);
0106
0107
0108 ReadResourceInfoFile();
0109
0110
0111
0112 overide_URL_base = false;
0113 if (params != nullptr) {
0114 if (params->Exists("JANA:RESOURCE_URL")) {
0115 overide_URL_base = true;
0116 URL_base = "";
0117 params->SetDefaultParameter("JANA:RESOURCE_URL", URL_base,
0118 "Base URL to use for retrieving resources. If set, this will override any URL_base values found in the calib DB.");
0119 }
0120 }
0121
0122 #ifdef HAVE_CURL
0123
0124 curl_global_init(CURL_GLOBAL_ALL);
0125 curl_args = "";
0126 #else
0127
0128 curl_args = "--insecure";
0129 if (params)
0130 params->SetDefaultParameter("JANA:CURL_ARGS", curl_args,
0131 "additional arguments to be passed to the external curl program");
0132 #endif
0133
0134
0135 check_md5 = true;
0136 if (params)
0137 params->SetDefaultParameter("JANA:RESOURCE_CHECK_MD5", check_md5,
0138 "Set this to 0 to disable checking of the md5 checksum for resource files. You generally want this check left on.");
0139 }
0140
0141
0142
0143
0144 JResource::~JResource() {
0145 if (jcalibfile) delete jcalibfile;
0146
0147 #ifdef HAVE_CURL
0148
0149 curl_global_cleanup();
0150 #endif
0151 }
0152
0153
0154
0155
0156 string JResource::GetResource(string namepath) {
0157 string fullpath = GetLocalPathToResource(namepath);
0158
0159
0160
0161 if (jcalib) {
0162
0163
0164 map<string, string> info;
0165 jcalib->Get(namepath, info);
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
0192
0193
0194
0195 bool has_URL_base = info.find("URL_base") != info.end();
0196 bool has_path = info.find("path") != info.end();
0197 bool has_URL = info.find("URL") != info.end();
0198 bool has_md5 = info.find("md5") != info.end();
0199
0200 if (overide_URL_base) has_URL_base = true;
0201
0202 string URL = "";
0203 string path = namepath;
0204
0205
0206 if (has_URL_base || has_path) {
0207 if (!has_URL_base) {
0208 jout << "URL_base=\"" << info["URL_base"] << "\" path=\"" << info["path"] << "\"" << endl;
0209 string mess = string("\"path\" specified for resource \"") + namepath + "\" but not \"URL_base\"!";
0210 throw JException(mess);
0211 }
0212 if (!has_path) {
0213 jout << "URL_base=\"" << info["URL_base"] << "\" path=\"" << info["path"] << "\"" << endl;
0214 string mess = string("\"URL_base\" specified for resource \"") + namepath + "\" but not \"path\"!";
0215 throw JException(mess);
0216 }
0217
0218 string my_URL_base = info["URL_base"];
0219 if (overide_URL_base) my_URL_base = URL_base;
0220 if (my_URL_base.length() == 0) {
0221 my_URL_base += "/";
0222 } else {
0223 if (my_URL_base[my_URL_base.length() - 1] != '/') my_URL_base += "/";
0224 }
0225
0226 path = info["path"];
0227 if (path.length() > 0)
0228 if (path[0] == '/') path.erase(0, 1);
0229
0230 URL = my_URL_base + path;
0231
0232
0233
0234 } else if (has_URL) {
0235
0236 URL = info["URL"];
0237
0238
0239
0240 } else {
0241 string mess =
0242 string("Neither \"URL_base\",\"path\" pair nor \"URL\" exist in DB for resource \"") + namepath +
0243 "\" !";
0244 throw JException(mess);
0245 }
0246
0247
0248
0249 pthread_mutex_lock(&resource_manager_mutex);
0250 if (resources.find(URL) != resources.end()) {
0251 fullpath = resource_dir + "/" + resources[URL];
0252 }
0253 pthread_mutex_unlock(&resource_manager_mutex);
0254
0255
0256 bool file_exists = false;
0257 ifstream ifs(fullpath.c_str());
0258 if (ifs.is_open()) {
0259 file_exists = true;
0260 ifs.close();
0261 }
0262
0263
0264
0265
0266
0267
0268
0269
0270
0271
0272
0273 bool check_for_redownload = true;
0274 if ((!file_exists) && has_path) {
0275 string alternate_fullpath = resource_dir + "/" + info["path"];
0276 ifstream ifs(alternate_fullpath.c_str());
0277 if (ifs.is_open()) {
0278 ifs.close();
0279 file_exists = true;
0280 fullpath = alternate_fullpath;
0281 check_for_redownload = false;
0282 }
0283 }
0284
0285
0286 bool rewrite_info_file = false;
0287
0288
0289 if (!file_exists) {
0290 GetResourceFromURL(URL, fullpath);
0291
0292 pthread_mutex_lock(&resource_manager_mutex);
0293 resources[URL] = path;
0294 pthread_mutex_unlock(&resource_manager_mutex);
0295
0296 rewrite_info_file = true;
0297 } else if (check_for_redownload) {
0298
0299
0300 bool redownload_required = false;
0301 string other_URL = "";
0302 map<string, string>::iterator iter;
0303 pthread_mutex_lock(&resource_manager_mutex);
0304 for (iter = resources.begin(); iter != resources.end(); iter++) {
0305 if (iter->second == path) {
0306 if (iter->first != URL) {
0307 other_URL = iter->first;
0308 redownload_required = true;
0309 break;
0310 }
0311 }
0312 }
0313 pthread_mutex_unlock(&resource_manager_mutex);
0314
0315 if (redownload_required) {
0316
0317
0318 jout << " Resource \"" << namepath << "\" already exists, but is" << endl;
0319 jout << " associated with the URL: " << other_URL << endl;
0320 jout << " Deleting existing file and downloading new version" << endl;
0321 jout << " from: " << URL << endl;
0322 unlink(fullpath.c_str());
0323
0324 GetResourceFromURL(URL, fullpath);
0325
0326 pthread_mutex_lock(&resource_manager_mutex);
0327 resources[URL] = path;
0328
0329
0330
0331
0332 for (iter = resources.begin(); iter != resources.end(); ) {
0333 map<string, string>::iterator tmp = iter;
0334 iter++;
0335 if (tmp->second == path) {
0336 if (tmp->first != URL)resources.erase(tmp);
0337 }
0338 }
0339 pthread_mutex_unlock(&resource_manager_mutex);
0340
0341 rewrite_info_file = true;
0342 }
0343 }
0344
0345
0346 if (has_md5 && check_md5) {
0347 string md5sum = Get_MD5(fullpath);
0348 if (md5sum != info["md5"]) {
0349 jerr << "-- ERROR: md5 checksum for the following resource file does not match expected" << endl;
0350 jerr << "-- " << fullpath << endl;
0351 jerr << "-- for the resource: " << endl;
0352 if (has_URL_base) jerr << "-- URL_base = " << info["URL_base"] << endl;
0353 if (has_path) jerr << "-- path = " << info["path"] << endl;
0354 if (has_URL) jerr << "-- URL = " << info["URL"] << endl;
0355 if (has_md5) jerr << "-- md5 = " << info["md5"] << endl;
0356 jerr << "-- The md5sum for the existing file is: " << md5sum << endl;
0357 jerr << "--" << endl;
0358 jerr << "-- This can happen if the resource download was previously interrupted." << endl;
0359 jerr << "-- Try removing the existing file and re-running to trigger a re-download." << endl;
0360 jerr << "--" << endl;
0361 jerr << "-- This is a fatal error and the program will stop now. To bypass checking" << endl;
0362 jerr << "-- the md5sum, set the JANA:RESOURCE_CHECK_MD5 config. parameter to 0." << endl;
0363 jerr << "--" << endl;
0364 exit(-1);
0365 }
0366 }
0367
0368
0369 if (rewrite_info_file) WriteResourceInfoFile();
0370 }
0371
0372 return fullpath;
0373 }
0374
0375
0376
0377
0378 string JResource::GetLocalPathToResource(string namepath) {
0379 string fullpath = resource_dir + "/" + namepath;
0380 if (jcalib) {
0381 for (unsigned int i = 0; i < calib_namepaths.size(); i++) {
0382 if (calib_namepaths[i] == namepath) {
0383 map<string, string> info;
0384 jcalib->Get(namepath, info);
0385
0386 if (info.find("path") != info.end()) {
0387 fullpath = resource_dir + "/" + info["path"];
0388 }
0389 break;
0390 }
0391 }
0392 }
0393
0394 return fullpath;
0395 }
0396
0397
0398
0399
0400 void JResource::ReadResourceInfoFile(void) {
0401 pthread_mutex_lock(&resource_manager_mutex);
0402
0403
0404
0405 resources.clear();
0406
0407
0408 string fname = GetLocalPathToResource("resources");
0409 ifstream ifs(fname.c_str());
0410 if (!ifs.is_open()) {
0411 pthread_mutex_unlock(&resource_manager_mutex);
0412 return;
0413 }
0414 ifs.close();
0415
0416
0417
0418 jcalibfile->Get("resources", resources);
0419
0420 pthread_mutex_unlock(&resource_manager_mutex);
0421 }
0422
0423
0424
0425
0426 void JResource::WriteResourceInfoFile(void) {
0427 pthread_mutex_lock(&resource_manager_mutex);
0428
0429
0430 string fname = GetLocalPathToResource("resources");
0431
0432
0433 ofstream ofs(fname.c_str(), ios_base::out | ios_base::trunc);
0434
0435
0436 time_t t = time(NULL);
0437 ofs << "#" << endl;
0438 ofs << "# JANA resources file Auto-generated DO NOT EDIT" << endl;
0439 ofs << "#" << endl;
0440 ofs << "# " << ctime(&t);
0441 ofs << "#" << endl;
0442 ofs << "#% URL namepath" << endl;
0443
0444 map<string, string>::iterator iter;
0445 for (iter = resources.begin(); iter != resources.end(); iter++) {
0446 ofs << iter->first << "\t" << iter->second << endl;
0447 }
0448 ofs << endl;
0449
0450
0451 ofs.close();
0452
0453 pthread_mutex_unlock(&resource_manager_mutex);
0454 }
0455
0456
0457
0458
0459 void JResource::GetResourceFromURL(const string &URL, const string &fullpath) {
0460
0461
0462
0463
0464 pthread_mutex_lock(&resource_manager_mutex);
0465
0466 jout << "Downloading " << URL << " ..." << endl;
0467 CURRENT_OUTPUT_FNAME = fullpath;
0468 if (fullpath.length() > 60) {
0469 CURRENT_OUTPUT_FNAME = string("...") + fullpath.substr(fullpath.length() - 60, 60);
0470 }
0471
0472
0473 char tmp[256];
0474 strcpy(tmp, fullpath.c_str());
0475 char *path_only = dirname(tmp);
0476 mkpath(path_only);
0477
0478
0479
0480 string info_xml = resource_dir + "/info.xml";
0481 ofstream ofs(info_xml.c_str());
0482 ofs.close();
0483
0484 #ifdef HAVE_CURL
0485
0486
0487
0488 CURL *curl = curl_easy_init();
0489
0490
0491 char error[CURL_ERROR_SIZE] = "";
0492 FILE *f = fopen(fullpath.c_str(), "w");
0493 curl_easy_setopt(curl, CURLOPT_VERBOSE, 0);
0494 curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
0495 curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
0496 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
0497 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
0498 curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, mycurl_printprogress);
0499 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error);
0500
0501
0502
0503 curl_easy_perform(curl);
0504 if(error[0]!=0) cout << error << endl;
0505
0506
0507 curl_easy_cleanup(curl);
0508
0509
0510 cout << endl;
0511 fclose(f);
0512
0513 #else
0514
0515
0516 string cmd = "curl " + curl_args + " " + URL + " -o " + fullpath;
0517 cout << cmd << endl;
0518 system(cmd.c_str());
0519 #endif
0520
0521
0522
0523
0524
0525
0526
0527 pthread_mutex_unlock(&resource_manager_mutex);
0528 }
0529
0530
0531
0532
0533 string JResource::Get_MD5(string fullpath) {
0534 ifstream ifs(fullpath.c_str());
0535 if (!ifs.is_open()) return string("");
0536
0537 md5_state_t pms;
0538 md5_init(&pms);
0539
0540
0541 unsigned int buffer_size = 10000;
0542 char *buff = new char[buffer_size];
0543
0544
0545 while (ifs.good()) {
0546 ifs.read(buff, buffer_size);
0547 md5_append(&pms, (const md5_byte_t *) buff, ifs.gcount());
0548 }
0549 ifs.close();
0550
0551 delete[] buff;
0552
0553 md5_byte_t digest[16];
0554 md5_finish(&pms, digest);
0555
0556 const size_t str_len = 16 * 2 + 1;
0557 char hex_output[str_len];
0558 for (int di = 0; di < 16; ++di){
0559 size_t buff_left = str_len - di * 2;
0560 snprintf(hex_output + di * 2, buff_left, "%02x", digest[di]);
0561 }
0562
0563 return string(hex_output);
0564 }
0565
0566
0567
0568
0569
0570
0571
0572
0573
0574 int mkpath(string s, mode_t mode) {
0575 size_t pre = 0, pos;
0576 std::string dir;
0577 int mdret = 0;
0578
0579 if (s[s.size() - 1] != '/') {
0580
0581 s += '/';
0582 }
0583
0584 while ((pos = s.find_first_of('/', pre)) != string::npos) {
0585 dir = s.substr(0, pos++);
0586 pre = pos;
0587 if (dir.size() == 0) continue;
0588 if ((mdret = mkdir(dir.c_str(), mode)) && errno != EEXIST) {
0589 return mdret;
0590 }
0591 }
0592 return mdret;
0593 }
0594
0595 #ifdef HAVE_CURL
0596
0597
0598
0599 int mycurl_printprogress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
0600 {
0601 unsigned long kB_downloaded = (unsigned long)(dlnow/1024.0);
0602 cout << " " << kB_downloaded << "kB " << CURRENT_OUTPUT_FNAME << "\r";
0603 cout.flush();
0604
0605 return 0;
0606 }
0607 #endif
0608