XRootD
Loading...
Searching...
No Matches
XrdPfcConfiguration.cc
Go to the documentation of this file.
1#include "XrdPfc.hh"
2#include "XrdPfcTrace.hh"
3#include "XrdPfcInfo.hh"
4
6#include "XrdPfcPurgePin.hh"
7
8#include "XrdOss/XrdOss.hh"
9
10#include "XrdOuc/XrdOucEnv.hh"
11#include "XrdOuc/XrdOucUtils.hh"
14#include "XrdOuc/XrdOuca2x.hh"
15
16#include "XrdVersion.hh"
18#include "XrdSys/XrdSysXAttr.hh"
19
20#include <fcntl.h>
21
23
24namespace XrdPfc
25{
26 const char *trace_what_strings[] = {"","error ","warning ","info ","debug ","dump "};
27}
28
29using namespace XrdPfc;
30
32
64
65
66bool Cache::cfg2bytes(const std::string &str, long long &store, long long totalSpace, const char *name) const
67{
68 char errStr[1024];
69 snprintf(errStr, 1024, "ConfigParameters() Error parsing parameter %s", name);
70
71 if (::isalpha(*(str.rbegin())))
72 {
73 if (XrdOuca2x::a2sz(m_log, errStr, str.c_str(), &store, 0, totalSpace))
74 {
75 return false;
76 }
77 }
78 else
79 {
80 char *eP;
81 errno = 0;
82 double frac = strtod(str.c_str(), &eP);
83 if (errno || eP == str.c_str())
84 {
85 m_log.Emsg(errStr, str.c_str());
86 return false;
87 }
88
89 store = static_cast<long long>(totalSpace * frac + 0.5);
90 }
91
92 if (store < 0 || store > totalSpace)
93 {
94 snprintf(errStr, 1024, "ConfigParameters() Error: parameter %s should be between 0 and total available disk space (%lld) - it is %lld (given as %s)",
95 name, totalSpace, store, str.c_str());
96 m_log.Emsg(errStr, "");
97 return false;
98 }
99
100 return true;
101}
102
103bool Cache::blocksize_str2value(const char *from, const char *str,
104 long long &val, long long min, long long max) const
105{
106 if (XrdOuca2x::a2sz(m_log, "Error parsing block-size", str, &val, min, max))
107 return false;
108
109 if (val & 0xFFF) {
110 val &= ~0x0FFF;
111 val += 0x1000;
112 m_log.Emsg(from, "blocksize must be a multiple of 4 kB. Rounded up.");
113 }
114
115 return true;
116}
117
118bool Cache::prefetch_str2value(const char *from, const char *str,
119 int &val, int min, int max) const
120{
121 if (XrdOuca2x::a2i(m_log, "Error parsing prefetch block count", str, &val, min, max))
122 return false;
123
124 return true;
125}
126
127/* Function: xcschk
128
129 Purpose: To parse the directive: cschk <parms>
130
131 parms: [[no]net] [[no]tls] [[no]cache] [uvkeep <arg>]
132
133 all Checksum check on cache & net transfers.
134 cache Checksum check on cache only, 'no' turns it off.
135 net Checksum check on net transfers 'no' turns it off.
136 tls use TLS if server doesn't support checksums 'no' turns it off.
137 uvkeep Maximum amount of time a cached file make be kept if it
138 contains unverified checksums as n[d|h|m|s], where 'n'
139 is a non-negative integer. A value of 0 prohibits disk
140 caching unless the checksum can be verified. You can
141 also specify "lru" which means the standard purge policy
142 is to be used.
143
144 Output: true upon success or false upon failure.
145 */
146bool Cache::xcschk(XrdOucStream &Config)
147{
148 const char *val, *val2;
149 struct cschkopts {const char *opname; int opval;} csopts[] =
150 {
151 {"off", CSChk_None},
152 {"cache", CSChk_Cache},
153 {"net", CSChk_Net},
154 {"tls", CSChk_TLS}
155 };
156 int i, numopts = sizeof(csopts)/sizeof(struct cschkopts);
157 bool isNo;
158
159 if (! (val = Config.GetWord()))
160 {m_log.Emsg("Config", "cschk parameter not specified"); return false; }
161
162 while(val)
163 {
164 if ((isNo = strncmp(val, "no", 2) == 0))
165 val2 = val + 2;
166 else
167 val2 = val;
168 for (i = 0; i < numopts; i++)
169 {
170 if (!strcmp(val2, csopts[i].opname))
171 {
172 if (isNo)
173 m_configuration.m_cs_Chk &= ~csopts[i].opval;
174 else if (csopts[i].opval)
175 m_configuration.m_cs_Chk |= csopts[i].opval;
176 else
177 m_configuration.m_cs_Chk = csopts[i].opval;
178 break;
179 }
180 }
181 if (i >= numopts)
182 {
183 if (strcmp(val, "uvkeep"))
184 {
185 m_log.Emsg("Config", "invalid cschk option -", val);
186 return false;
187 }
188 if (!(val = Config.GetWord()))
189 {
190 m_log.Emsg("Config", "cschk uvkeep value not specified");
191 return false;
192 }
193 if (!strcmp(val, "lru"))
194 m_configuration.m_cs_UVKeep = -1;
195 else
196 {
197 int uvkeep;
198 if (XrdOuca2x::a2tm(m_log, "uvkeep time", val, &uvkeep, 0))
199 return false;
200 m_configuration.m_cs_UVKeep = uvkeep;
201 }
202 }
203 val = Config.GetWord();
204 }
205 // Decompose into separate TLS state, it is only passed on to psx
206 m_configuration.m_cs_ChkTLS = m_configuration.m_cs_Chk & CSChk_TLS;
207 m_configuration.m_cs_Chk &= ~CSChk_TLS;
208
209 m_env->Put("psx.CSNet", m_configuration.is_cschk_net() ? (m_configuration.m_cs_ChkTLS ? "2" : "1") : "0");
210
211 return true;
212}
213
214
215/* Function: xdlib
216
217 Purpose: To parse the directive: decisionlib <path> [<parms>]
218
219 <path> the path of the decision library to be used.
220 <parms> optional parameters to be passed.
221
222
223 Output: true upon success or false upon failure.
224 */
225bool Cache::xdlib(XrdOucStream &Config)
226{
227 const char* val;
228
229 std::string libp;
230 if (! (val = Config.GetWord()) || ! val[0])
231 {
232 TRACE(Info," Cache::Config() decisionlib not specified; always caching files");
233 return true;
234 }
235 else
236 {
237 libp = val;
238 }
239
240 char params[4096];
241 if (val[0])
242 Config.GetRest(params, 4096);
243 else
244 params[0] = 0;
245
246 XrdOucPinLoader* myLib = new XrdOucPinLoader(&m_log, 0, "decisionlib",
247 libp.c_str());
248
249 Decision *(*ep)(XrdSysError&);
250 ep = (Decision *(*)(XrdSysError&))myLib->Resolve("XrdPfcGetDecision");
251 if (! ep) {myLib->Unload(true); return false; }
252
253 Decision * d = ep(m_log);
254 if (! d)
255 {
256 TRACE(Error, "Config() decisionlib was not able to create a decision object");
257 return false;
258 }
259 if (params[0])
260 d->ConfigDecision(params);
261
262 m_decisionpoints.push_back(d);
263 return true;
264}
265
266/* Function: xplib
267
268 Purpose: To parse the directive: purgelib <path> [<parms>]
269
270 <path> the path of the decision library to be used.
271 <parms> optional parameters to be passed.
272
273
274 Output: true upon success or false upon failure.
275 */
276bool Cache::xplib(XrdOucStream &Config)
277{
278 const char* val;
279
280 std::string libp;
281 if (! (val = Config.GetWord()) || ! val[0])
282 {
283 TRACE(Info," Cache::Config() purgelib not specified; will use LRU for purging files");
284 return true;
285 }
286 else
287 {
288 libp = val;
289 }
290
291 char params[4096];
292 if (val[0])
293 Config.GetRest(params, 4096);
294 else
295 params[0] = 0;
296
297 XrdOucPinLoader* myLib = new XrdOucPinLoader(&m_log, 0, "purgelib",
298 libp.c_str());
299
300 PurgePin *(*ep)(XrdSysError&);
301 ep = (PurgePin *(*)(XrdSysError&))myLib->Resolve("XrdPfcGetPurgePin");
302 if (! ep) {myLib->Unload(true); return false; }
303
304 PurgePin * dp = ep(m_log);
305 if (! dp)
306 {
307 TRACE(Error, "Config() purgelib was not able to create a Purge Plugin object?");
308 return false;
309 }
310 m_purge_pin = dp;
311
312 if (params[0])
313 m_purge_pin->ConfigPurgePin(params);
314
315
316 return true;
317}
318
319/* Function: xtrace
320
321 Purpose: To parse the directive: trace <level>
322 Output: true upon success or false upon failure.
323 */
324bool Cache::xtrace(XrdOucStream &Config)
325{
326 char *val;
327 static struct traceopts {const char *opname; int opval; } tropts[] =
328 {
329 {"none", 0},
330 {"error", 1},
331 {"warning", 2},
332 {"info", 3},
333 {"debug", 4},
334 {"dump", 5},
335 {"dumpxl", 6}
336 };
337 int numopts = sizeof(tropts)/sizeof(struct traceopts);
338
339 if (! (val = Config.GetWord()))
340 {m_log.Emsg("Config", "trace option not specified"); return 1; }
341
342 for (int i = 0; i < numopts; i++)
343 {
344 if (! strcmp(val, tropts[i].opname))
345 {
346 m_trace->What = tropts[i].opval;
347 return true;
348 }
349 }
350 m_log.Emsg("Config", "invalid trace option -", val);
351 return false;
352}
353
354// Determine if oss spaces are operational and if they support xattrs.
355bool Cache::test_oss_basics_and_features()
356{
357 static const char *epfx = "test_oss_basics_and_features()";
358
359 const auto &conf = m_configuration;
360 const char *user = conf.m_username.c_str();
361 XrdOucEnv env;
362
363 auto check_space = [&](const char *space, bool &has_xattr)
364 {
365 std::string fname("__prerun_test_pfc_");
366 fname += space;
367 fname += "_space__";
368 env.Put("oss.cgroup", space);
369
370 int res = m_oss->Create(user, fname.c_str(), 0600, env, XRDOSS_mkpath);
371 if (res != XrdOssOK) {
372 m_log.Emsg(epfx, "Can not create a file on space", space);
373 return false;
374 }
375 XrdOssDF *oss_file = m_oss->newFile(user);
376 res = oss_file->Open(fname.c_str(), O_RDWR, 0600, env);
377 if (res != XrdOssOK) {
378 m_log.Emsg(epfx, "Can not open a file on space", space);
379 return false;
380 }
381 res = oss_file->Write(fname.data(), 0, fname.length());
382 if (res != (int) fname.length()) {
383 m_log.Emsg(epfx, "Can not write into a file on space", space);
384 return false;
385 }
386
387 has_xattr = true;
388 long long fsize = fname.length();
389 res = XrdSysXAttrActive->Set("pfc.fsize", &fsize, sizeof(long long), 0, oss_file->getFD(), 0);
390 if (res != 0) {
391 m_log.Emsg(epfx, "Can not write xattr to a file on space", space);
392 has_xattr = false;
393 }
394
395 oss_file->Close();
396
397 if (has_xattr) {
398 char pfn[4096];
399 m_oss->Lfn2Pfn(fname.c_str(), pfn, 4096);
400 fsize = -1ll;
401 res = XrdSysXAttrActive->Get("pfc.fsize", &fsize, sizeof(long long), pfn);
402 if (res != sizeof(long long) || fsize != (long long) fname.length())
403 {
404 m_log.Emsg(epfx, "Can not read xattr from a file on space", space);
405 has_xattr = false;
406 }
407 }
408
409 res = m_oss->Unlink(fname.c_str());
410 if (res != XrdOssOK) {
411 m_log.Emsg(epfx, "Can not unlink a file on space", space);
412 return false;
413 }
414
415 return true;
416 };
417
418 bool aOK = true;
419 aOK &= check_space(conf.m_data_space.c_str(), m_dataXattr);
420 aOK &= check_space(conf.m_meta_space.c_str(), m_metaXattr);
421
422 return aOK;
423}
424
425//______________________________________________________________________________
426/* Function: Config
427
428 Purpose: To parse configuration file and configure Cache instance.
429 Output: true upon success or false upon failure.
430 */
431bool Cache::Config(const char *config_filename, const char *parameters, XrdOucEnv *env)
432{
433 // Indicate whether or not we are a client instance
434 const char *theINS = getenv("XRDINSTANCE");
435 m_isClient = (theINS != 0 && strncmp("*client ", theINS, 8) == 0);
436
437 // Tell everyone else we are a caching proxy
438 XrdOucEnv::Export("XRDPFC", 1);
439
440 XrdOucEnv emptyEnv;
441 XrdOucEnv *myEnv = env ? env : &emptyEnv;
442
443 XrdOucStream Config(&m_log, theINS, myEnv, "=====> ");
444
445 if (! config_filename || ! *config_filename)
446 {
447 TRACE(Error, "Config() configuration file not specified.");
448 return false;
449 }
450
451 int fd;
452 if ( (fd = open(config_filename, O_RDONLY, 0)) < 0)
453 {
454 TRACE( Error, "Config() can't open configuration file " << config_filename);
455 return false;
456 }
457
458 Config.Attach(fd);
459 static const char *cvec[] = { "*** pfc plugin config:", 0 };
460 Config.Capture(cvec);
461
462 // Obtain OFS configurator for OSS plugin.
463 XrdOfsConfigPI *ofsCfg = XrdOfsConfigPI::New(config_filename,&Config,&m_log,
464 &XrdVERSIONINFOVAR(XrdOucGetCache));
465 if (! ofsCfg) return false;
466
467 TmpConfiguration tmpc;
468
469 Configuration &CFG = m_configuration;
470
471 // Adjust default parameters for client/serverless caching
472 if (m_isClient)
473 {
474 m_configuration.m_bufferSize = 128 * 1024; // same as normal.
475 m_configuration.m_wqueue_blocks = 8;
476 m_configuration.m_wqueue_threads = 1;
477 }
478
479 // If network checksum processing is the default, indicate so.
480 if (m_configuration.is_cschk_net()) m_env->Put("psx.CSNet", m_configuration.m_cs_ChkTLS ? "2" : "1");
481
482 // Actual parsing of the config file.
483 bool retval = true, aOK = true;
484 char *var;
485 while ((var = Config.GetMyFirstWord()))
486 {
487 if (! strcmp(var,"pfc.osslib"))
488 {
489 retval = ofsCfg->Parse(XrdOfsConfigPI::theOssLib);
490 }
491 else if (! strcmp(var,"pfc.cschk"))
492 {
493 retval = xcschk(Config);
494 }
495 else if (! strcmp(var,"pfc.decisionlib"))
496 {
497 retval = xdlib(Config);
498 }
499 else if (! strcmp(var,"pfc.purgelib"))
500 {
501 retval = xplib(Config);
502 }
503 else if (! strcmp(var,"pfc.trace"))
504 {
505 retval = xtrace(Config);
506 }
507 else if (! strcmp(var,"pfc.allow_xrdpfc_command"))
508 {
509 m_configuration.m_allow_xrdpfc_command = true;
510 }
511 else if (! strncmp(var,"pfc.", 4))
512 {
513 retval = ConfigParameters(std::string(var+4), Config, tmpc);
514 }
515
516 if ( ! retval)
517 {
518 TRACE(Error, "Config() error in parsing");
519 aOK = false;
520 }
521 }
522
523 Config.Close();
524
525 // Load OSS plugin.
526 auto orig_runmode = myEnv->Get("oss.runmode");
527 myEnv->Put("oss.runmode", "pfc");
528 if (m_configuration.is_cschk_cache())
529 {
530 char csi_conf[128];
531 if (snprintf(csi_conf, 128, "space=%s nofill", m_configuration.m_meta_space.c_str()) < 128)
532 {
533 ofsCfg->Push(XrdOfsConfigPI::theOssLib, "libXrdOssCsi.so", csi_conf);
534 } else {
535 TRACE(Error, "Config() buffer too small for libXrdOssCsi params.");
536 return false;
537 }
538 }
539 if (ofsCfg->Load(XrdOfsConfigPI::theOssLib, myEnv))
540 {
541 ofsCfg->Plugin(m_oss);
542 }
543 else
544 {
545 TRACE(Error, "Config() Unable to create an OSS object");
546 return false;
547 }
548 if (orig_runmode) myEnv->Put("oss.runmode", orig_runmode);
549 else myEnv->Put("oss.runmode", "");
550
551 // Test if OSS is operational, determine optional features.
552 aOK &= test_oss_basics_and_features();
553
554 // sets default value for disk usage
555 XrdOssVSInfo sP;
556 {
557 if (m_configuration.m_meta_space != m_configuration.m_data_space &&
558 m_oss->StatVS(&sP, m_configuration.m_meta_space.c_str(), 1) < 0)
559 {
560 m_log.Emsg("ConfigParameters()", "error obtaining stat info for meta space ", m_configuration.m_meta_space.c_str());
561 return false;
562 }
563 if (m_configuration.m_meta_space != m_configuration.m_data_space && sP.Total < 10ll << 20)
564 {
565 m_log.Emsg("ConfigParameters()", "available data space is less than 10 MB (can be due to a mistake in oss.localroot directive) for space ",
566 m_configuration.m_meta_space.c_str());
567 return false;
568 }
569 if (m_oss->StatVS(&sP, m_configuration.m_data_space.c_str(), 1) < 0)
570 {
571 m_log.Emsg("ConfigParameters()", "error obtaining stat info for data space ", m_configuration.m_data_space.c_str());
572 return false;
573 }
574 if (sP.Total < 10ll << 20)
575 {
576 m_log.Emsg("ConfigParameters()", "available data space is less than 10 MB (can be due to a mistake in oss.localroot directive) for space ",
577 m_configuration.m_data_space.c_str());
578 return false;
579 }
580
581 m_configuration.m_diskTotalSpace = sP.Total;
582
583 if (cfg2bytes(tmpc.m_diskUsageLWM, m_configuration.m_diskUsageLWM, sP.Total, "lowWatermark") &&
584 cfg2bytes(tmpc.m_diskUsageHWM, m_configuration.m_diskUsageHWM, sP.Total, "highWatermark"))
585 {
586 if (m_configuration.m_diskUsageLWM >= m_configuration.m_diskUsageHWM) {
587 m_log.Emsg("ConfigParameters()", "pfc.diskusage should have lowWatermark < highWatermark.");
588 aOK = false;
589 }
590 }
591 else aOK = false;
592
593 if ( ! tmpc.m_fileUsageMax.empty())
594 {
595 if (cfg2bytes(tmpc.m_fileUsageBaseline, m_configuration.m_fileUsageBaseline, sP.Total, "files baseline") &&
596 cfg2bytes(tmpc.m_fileUsageNominal, m_configuration.m_fileUsageNominal, sP.Total, "files nominal") &&
597 cfg2bytes(tmpc.m_fileUsageMax, m_configuration.m_fileUsageMax, sP.Total, "files max"))
598 {
599 if (m_configuration.m_fileUsageBaseline >= m_configuration.m_fileUsageNominal ||
600 m_configuration.m_fileUsageBaseline >= m_configuration.m_fileUsageMax ||
601 m_configuration.m_fileUsageNominal >= m_configuration.m_fileUsageMax)
602 {
603 m_log.Emsg("ConfigParameters()", "pfc.diskusage files should have baseline < nominal < max.");
604 aOK = false;
605 }
606
607
608 if (aOK && m_configuration.m_fileUsageMax >= m_configuration.m_diskUsageLWM)
609 {
610 m_log.Emsg("ConfigParameters()", "pfc.diskusage files values must be below lowWatermark");
611 aOK = false;
612 }
613 }
614 else aOK = false;
615 }
616 }
617
618 // sets flush frequency
619 if ( ! tmpc.m_flushRaw.empty())
620 {
621 if (::isalpha(*(tmpc.m_flushRaw.rbegin())))
622 {
623 if (XrdOuca2x::a2sz(m_log, "Error getting number of bytes written before flush", tmpc.m_flushRaw.c_str(),
624 &m_configuration.m_flushCnt,
625 100 * m_configuration.m_bufferSize , 100000 * m_configuration.m_bufferSize))
626 {
627 return false;
628 }
629 m_configuration.m_flushCnt /= m_configuration.m_bufferSize;
630 }
631 else
632 {
633 if (XrdOuca2x::a2ll(m_log, "Error getting number of blocks written before flush", tmpc.m_flushRaw.c_str(),
634 &m_configuration.m_flushCnt, 100, 100000))
635 {
636 return false;
637 }
638 }
639 }
640
641 // get number of available RAM blocks after process configuration
642 if (m_configuration.m_RamAbsAvailable == 0)
643 {
644 m_configuration.m_RamAbsAvailable = m_isClient ? 256ll * 1024 * 1024 : 1024ll * 1024 * 1024;
645 char buff[1024];
646 snprintf(buff, sizeof(buff), "RAM usage pfc.ram is not specified. Default value %s is used.", m_isClient ? "256m" : "1g");
647 m_log.Say("Config info: ", buff);
648 }
649 // Setup number of standard-size blocks not released back to the system to 5% of total RAM.
650 m_configuration.m_RamKeepStdBlocks = (m_configuration.m_RamAbsAvailable / m_configuration.m_bufferSize + 1) * 5 / 100;
651
652 // Set tracing to debug if this is set in environment
653 char* cenv = getenv("XRDDEBUG");
654 if (cenv && ! strcmp(cenv,"1") && m_trace->What < 4) m_trace->What = 4;
655
656 if (aOK)
657 {
658// 000 001 010
659 const char *csc[] = {"off", "cache nonet", "nocache net notls",
660// 011
661 "cache net notls",
662// 100 101 110
663 "off", "cache nonet", "nocache net tls",
664// 111
665 "cache net tls"};
666 char uvk[32];
667 if (m_configuration.m_cs_UVKeep < 0)
668 strcpy(uvk, "lru");
669 else
670 sprintf(uvk, "%lld", (long long) m_configuration.m_cs_UVKeep);
671 float ram_gb = (m_configuration.m_RamAbsAvailable) / float(1024*1024*1024);
672
673 char urlcgi_blks[64] = "ignore", urlcgi_npref[32] = "ignore";
675 snprintf(urlcgi_blks, sizeof(urlcgi_blks), "%lldk %lldk",
676 CFG.m_cgi_min_bufferSize >> 10, CFG.m_cgi_max_bufferSize >> 10);
678 snprintf(urlcgi_npref, sizeof(urlcgi_npref), "%d %d",
680
681 char buff[8192];
682 int loff = 0;
683 loff = snprintf(buff, sizeof(buff), "Config effective %s pfc configuration:\n"
684 " pfc.cschk %s uvkeep %s\n"
685 " pfc.blocksize %lldk\n"
686 " pfc.prefetch %d\n"
687 " pfc.urlcgi blocksize %s prefetch %s\n"
688 " pfc.ram %.fg\n"
689 " pfc.writequeue %d %d\n"
690 " # Total available disk: %lld\n"
691 " pfc.diskusage %lld %lld files %lld %lld %lld purgeinterval %d purgecoldfiles %d\n"
692 " pfc.spaces %s %s\n"
693 " pfc.trace %d\n"
694 " pfc.flush %lld\n"
695 " pfc.acchistorysize %d\n"
696 " pfc.onlyIfCachedMinBytes %lld\n"
697 " pfc.onlyIfCachedMinFrac %.2f\n",
698 config_filename,
699 csc[int(m_configuration.m_cs_Chk)], uvk,
700 m_configuration.m_bufferSize >> 10,
701 m_configuration.m_prefetch_max_blocks,
702 urlcgi_blks, urlcgi_npref,
703 ram_gb,
704 m_configuration.m_wqueue_blocks, m_configuration.m_wqueue_threads,
705 sP.Total,
706 m_configuration.m_diskUsageLWM, m_configuration.m_diskUsageHWM,
707 m_configuration.m_fileUsageBaseline, m_configuration.m_fileUsageNominal, m_configuration.m_fileUsageMax,
708 m_configuration.m_purgeInterval, m_configuration.m_purgeColdFilesAge,
709 m_configuration.m_data_space.c_str(),
710 m_configuration.m_meta_space.c_str(),
711 m_trace->What,
712 m_configuration.m_flushCnt,
713 m_configuration.m_accHistorySize,
714 m_configuration.m_onlyIfCachedMinSize,
715 m_configuration.m_onlyIfCachedMinFrac);
716
717 if (m_configuration.is_dir_stat_reporting_on())
718 {
719 loff += snprintf(buff + loff, sizeof(buff) - loff,
720 " pfc.dirstats interval %d maxdepth %d (internal: size_of_dirlist %d, size_of_globlist %d)\n",
721 m_configuration.m_dirStatsInterval, m_configuration.m_dirStatsStoreDepth,
722 (int) m_configuration.m_dirStatsDirs.size(), (int) m_configuration.m_dirStatsDirGlobs.size());
723 loff += snprintf(buff + loff, sizeof(buff) - loff, " dirlist:\n");
724 for (std::set<std::string>::iterator i = m_configuration.m_dirStatsDirs.begin(); i != m_configuration.m_dirStatsDirs.end(); ++i)
725 loff += snprintf(buff + loff, sizeof(buff) - loff, " %s\n", i->c_str());
726 loff += snprintf(buff + loff, sizeof(buff) - loff, " globlist:\n");
727 for (std::set<std::string>::iterator i = m_configuration.m_dirStatsDirGlobs.begin(); i != m_configuration.m_dirStatsDirGlobs.end(); ++i)
728 loff += snprintf(buff + loff, sizeof(buff) - loff, " %s/*\n", i->c_str());
729 }
730
731 if (m_configuration.m_hdfsmode)
732 {
733 loff += snprintf(buff + loff, sizeof(buff) - loff, " pfc.hdfsmode hdfsbsize %lld\n", m_configuration.m_hdfsbsize);
734 }
735
736 if (m_configuration.m_username.empty())
737 {
738 char unameBuff[256];
739 XrdOucUtils::UserName(getuid(), unameBuff, sizeof(unameBuff));
740 m_configuration.m_username = unameBuff;
741 }
742 else
743 {
744 loff += snprintf(buff + loff, sizeof(buff) - loff, " pfc.user %s\n", m_configuration.m_username.c_str());
745 }
746
747 m_log.Say(buff);
748
749 m_env->Put("XRDPFC.SEGSIZE", std::to_string(m_configuration.m_bufferSize).c_str());
750 }
751
752 // Derived settings
753 m_prefetch_enabled = CFG.m_prefetch_max_blocks > 0 || CFG.m_cgi_max_prefetch_max_blocks > 0;
755
756 m_gstream = (XrdXrootdGStream*) m_env->GetPtr("pfc.gStream*");
757
758 m_log.Say(" pfc g-stream has", m_gstream ? "" : " NOT", " been configured via xrootd.monitor directive\n");
759
760 // Create the ResourceMonitor and get it ready for starting the main thread function.
761 if (aOK)
762 {
763 m_res_mon = new ResourceMonitor(*m_oss);
764 m_res_mon->init_before_main();
765 }
766
767 m_log.Say("=====> Proxy file cache configuration parsing ", aOK ? "completed" : "failed");
768
769 if (ofsCfg) delete ofsCfg;
770
771 // XXXX-CKSUM Testing. To be removed after OssPgi is also merged and valildated.
772 // Building of xrdpfc_print fails when this is enabled.
773#ifdef XRDPFC_CKSUM_TEST
774 {
775 int xxx = m_configuration.m_cs_Chk;
776
777 for (m_configuration.m_cs_Chk = CSChk_None; m_configuration.m_cs_Chk <= CSChk_Both; ++m_configuration.m_cs_Chk)
778 {
779 Info::TestCksumStuff();
780 }
781
782 m_configuration.m_cs_Chk = xxx;
783 }
784#endif
785
786 return aOK;
787}
788
789//------------------------------------------------------------------------------
790
791bool Cache::ConfigParameters(std::string part, XrdOucStream& config, TmpConfiguration &tmpc)
792{
793 struct ConfWordGetter
794 {
795 XrdOucStream &m_config;
796 char *m_last_word;
797
798 ConfWordGetter(XrdOucStream& c) : m_config(c), m_last_word((char*)1) {}
799
800 const char* GetWord() { if (HasLast()) m_last_word = m_config.GetWord(); return HasLast() ? m_last_word : ""; }
801 bool HasLast() { return (m_last_word != 0); }
802 };
803
804 ConfWordGetter cwg(config);
805
806 Configuration &CFG = m_configuration;
807
808 if ( part == "user" )
809 {
810 m_configuration.m_username = cwg.GetWord();
811 if ( ! cwg.HasLast())
812 {
813 m_log.Emsg("Config", "Error: pfc.user requires a parameter.");
814 return false;
815 }
816 }
817 else if ( part == "diskusage" )
818 {
819 tmpc.m_diskUsageLWM = cwg.GetWord();
820 tmpc.m_diskUsageHWM = cwg.GetWord();
821
822 if (tmpc.m_diskUsageHWM.empty())
823 {
824 m_log.Emsg("Config", "Error: pfc.diskusage parameter requires at least two arguments.");
825 return false;
826 }
827
828 const char *p = 0;
829 while ((p = cwg.GetWord()) && cwg.HasLast())
830 {
831 if (strcmp(p, "files") == 0)
832 {
833 tmpc.m_fileUsageBaseline = cwg.GetWord();
834 tmpc.m_fileUsageNominal = cwg.GetWord();
835 tmpc.m_fileUsageMax = cwg.GetWord();
836
837 if ( ! cwg.HasLast())
838 {
839 m_log.Emsg("Config", "Error: pfc.diskusage files directive requires three arguments.");
840 return false;
841 }
842 }
843 else if (strcmp(p, "sleep") == 0 || strcmp(p, "purgeinterval") == 0)
844 {
845 if (strcmp(p, "sleep") == 0) m_log.Emsg("Config", "warning sleep directive is deprecated in pfc.diskusage. Please use purgeinterval instead.");
846
847 if (XrdOuca2x::a2tm(m_log, "Error getting purgeinterval", cwg.GetWord(), &m_configuration.m_purgeInterval, 60, 3600))
848 {
849 return false;
850 }
851 }
852 else if (strcmp(p, "purgecoldfiles") == 0)
853 {
854 if (XrdOuca2x::a2tm(m_log, "Error getting purgecoldfiles age", cwg.GetWord(), &m_configuration.m_purgeColdFilesAge, 3600, 3600*24*360))
855 {
856 return false;
857 }
858 if (XrdOuca2x::a2i(m_log, "Error getting purgecoldfiles period", cwg.GetWord(), &m_configuration.m_purgeAgeBasedPeriod, 1, 1000))
859 {
860 return false;
861 }
862 }
863 else
864 {
865 m_log.Emsg("Config", "Error: diskusage stanza contains unknown directive", p);
866 }
867 }
868 }
869 else if ( part == "acchistorysize" )
870 {
871 if ( XrdOuca2x::a2i(m_log, "Error getting access-history-size", cwg.GetWord(), &m_configuration.m_accHistorySize, 20, 200))
872 {
873 return false;
874 }
875 }
876 else if ( part == "dirstats" )
877 {
878 const char *p = 0;
879 while ((p = cwg.GetWord()) && cwg.HasLast())
880 {
881 if (strcmp(p, "interval") == 0)
882 {
883 int validIntervals[] = {60, 120, 300, 600, 900, 1200, 1800, 3600};
884 int size = sizeof(validIntervals) / sizeof(int);
885
886 if (XrdOuca2x::a2tm(m_log, "Error getting dirstsat interval", cwg.GetWord(),
887 &m_configuration.m_dirStatsInterval, validIntervals[0], validIntervals[size - 1]))
888 {
889 return false;
890 }
891 bool match = false, round_down = false;
892 for (int i = 0; i < size; i++) {
893 if (validIntervals[i] == m_configuration.m_dirStatsInterval) {
894 match = true;
895 break;
896 }
897 if (i > 0 && m_configuration.m_dirStatsInterval < validIntervals[i]) {
898 m_configuration.m_dirStatsInterval = validIntervals[i - 1];
899 round_down = true;
900 break;
901 }
902 }
903 if ( ! match && ! round_down) {
904 m_log.Emsg("Config", "Error: dirstat interval parsing failed.");
905 return false;
906 }
907 if (round_down) {
908 m_log.Emsg("Config", "Info: dirstat interval was rounded down to the nearest valid value.");
909 }
910
911 }
912 else if (strcmp(p, "maxdepth") == 0)
913 {
914 if (XrdOuca2x::a2i(m_log, "Error getting maxdepth value", cwg.GetWord(),
915 &m_configuration.m_dirStatsStoreDepth, 0, 16))
916 {
917 return false;
918 }
919 }
920 else if (strcmp(p, "dir") == 0)
921 {
922 p = cwg.GetWord();
923 if (p && p[0] == '/')
924 {
925 // XXX -- should we just store them as sets of PathTokenizer objects, not strings?
926
927 char d[1024]; d[0] = 0;
928 int depth = 0;
929 { // Compress multiple slashes and "measure" depth
930 const char *pp = p;
931 char *pd = d;
932 *(pd++) = *(pp++);
933 while (*pp != 0)
934 {
935 if (*(pd - 1) == '/')
936 {
937 if (*pp == '/')
938 {
939 ++pp; continue;
940 }
941 ++depth;
942 }
943 *(pd++) = *(pp++);
944 }
945 *(pd--) = 0;
946 // remove trailing but but not leading /
947 if (*pd == '/' && pd != d) *pd = 0;
948 }
949 int ld = strlen(d);
950 if (ld >= 2 && d[ld-1] == '*' && d[ld-2] == '/')
951 {
952 d[ld-2] = 0;
953 ld -= 2;
954 m_configuration.m_dirStatsDirGlobs.insert(d);
955 printf("Glob %s -> %s -- depth = %d\n", p, d, depth);
956 }
957 else
958 {
959 m_configuration.m_dirStatsDirs.insert(d);
960 printf("Dir %s -> %s -- depth = %d\n", p, d, depth);
961 }
962
963 m_configuration.m_dirStatsStoreDepth = std::max(m_configuration.m_dirStatsStoreDepth, depth);
964 }
965 else
966 {
967 m_log.Emsg("Config", "Error: dirstats dir parameter requires a directory argument starting with a '/'.");
968 return false;
969 }
970 }
971 else
972 {
973 m_log.Emsg("Config", "Error: dirstats stanza contains unknown directive '", p, "'");
974 return false;
975 }
976 }
977 }
978 else if ( part == "blocksize" )
979 {
980 if ( ! blocksize_str2value("Config", cwg.GetWord(), CFG.m_bufferSize,
982 return false;
983 }
984 else if ( part == "prefetch" || part == "nramprefetch" )
985 {
986 if (part == "nramprefetch")
987 {
988 m_log.Emsg("Config", "pfc.nramprefetch is deprecated, please use pfc.prefetch instead. Replacing the directive internally.");
989 }
990
991 if ( ! prefetch_str2value("Config", cwg.GetWord(), CFG.m_prefetch_max_blocks,
993 return false;
994 }
995 else if ( part == "urlcgi" )
996 {
997 // pfc.urlcgi [blocksize {ignore | min max}] [prefetch {ignore | min max}]
998 const char *p = 0;
999 while ((p = cwg.GetWord()) && cwg.HasLast())
1000 {
1001 if (strcmp(p, "blocksize") == 0)
1002 {
1003 std::string bmin = cwg.GetWord();
1004 if (bmin == "ignore")
1005 continue;
1006 std::string bmax = cwg.GetWord();
1007 if ( ! cwg.HasLast()) {
1008 m_log.Emsg("Config", "Error: pfc.urlcgi blocksize parameter requires two arguments.");
1009 return false;
1010 }
1011 if ( ! blocksize_str2value("Config::urlcgi", bmin.c_str(), CFG.m_cgi_min_bufferSize,
1013 return false;
1014 if ( ! blocksize_str2value("Config::urlcgi", bmax.c_str(), CFG.m_cgi_max_bufferSize,
1016 return false;
1018 m_log.Emsg("Config", "Error: pfc.urlcgi blocksize second argument must be larger or equal to the first one.");
1019 return false;
1020 }
1021 CFG.m_cgi_blocksize_allowed = true;
1022 }
1023 else if (strcmp(p, "prefetch") == 0)
1024 {
1025 std::string bmin = cwg.GetWord();
1026 if (bmin == "ignore")
1027 continue;
1028 std::string bmax = cwg.GetWord();
1029 if ( ! cwg.HasLast()) {
1030 m_log.Emsg("Config", "Error: pfc.urlcgi blocksize parameter requires two arguments.");
1031 return false;
1032 }
1033 if ( ! prefetch_str2value("Config::urlcgi", bmin.c_str(), CFG.m_cgi_min_prefetch_max_blocks,
1035 return false;
1036 if ( ! prefetch_str2value("Config::urlcgi", bmax.c_str(), CFG.m_cgi_max_prefetch_max_blocks,
1038 return false;
1040 m_log.Emsg("Config", "Error: pfc.urlcgi prefetch second argument must be larger or equal to the first one.");
1041 return false;
1042 }
1043 CFG.m_cgi_prefetch_allowed = true;
1044 }
1045 else
1046 {
1047 m_log.Emsg("Config", "Error: urlcgi stanza contains unknown directive '", p, "'");
1048 return false;
1049 }
1050 } // while get next pfc.urlcgi word
1051 }
1052 else if ( part == "nramread" )
1053 {
1054 m_log.Emsg("Config", "pfc.nramread is deprecated, please use pfc.ram instead. Ignoring this directive.");
1055 cwg.GetWord(); // Ignoring argument.
1056 }
1057 else if ( part == "ram" )
1058 {
1059 long long minRAM = m_isClient ? 256 * 1024 * 1024 : 1024 * 1024 * 1024;
1060 long long maxRAM = 256 * minRAM;
1061 if ( XrdOuca2x::a2sz(m_log, "get RAM available", cwg.GetWord(), &m_configuration.m_RamAbsAvailable, minRAM, maxRAM))
1062 {
1063 return false;
1064 }
1065 }
1066 else if ( part == "writequeue")
1067 {
1068 if (XrdOuca2x::a2i(m_log, "Error getting pfc.writequeue num-blocks", cwg.GetWord(), &m_configuration.m_wqueue_blocks, 1, 1024))
1069 {
1070 return false;
1071 }
1072 if (XrdOuca2x::a2i(m_log, "Error getting pfc.writequeue num-threads", cwg.GetWord(), &m_configuration.m_wqueue_threads, 1, 64))
1073 {
1074 return false;
1075 }
1076 }
1077 else if ( part == "spaces" )
1078 {
1079 m_configuration.m_data_space = cwg.GetWord();
1080 m_configuration.m_meta_space = cwg.GetWord();
1081 if ( ! cwg.HasLast())
1082 {
1083 m_log.Emsg("Config", "spacenames requires two parameters: <data-space> <metadata-space>.");
1084 return false;
1085 }
1086 }
1087 else if ( part == "hdfsmode" )
1088 {
1089 m_log.Emsg("Config", "pfc.hdfsmode is currently unsupported.");
1090 return false;
1091
1092 m_configuration.m_hdfsmode = true;
1093
1094 const char* params = cwg.GetWord();
1095 if (params)
1096 {
1097 if (! strncmp("hdfsbsize", params, 9))
1098 {
1099 long long minBlSize = 32 * 1024;
1100 long long maxBlSize = 128 * 1024 * 1024;
1101 if ( XrdOuca2x::a2sz(m_log, "Error getting file fragment size", cwg.GetWord(), &m_configuration.m_hdfsbsize, minBlSize, maxBlSize))
1102 {
1103 return false;
1104 }
1105 }
1106 else
1107 {
1108 m_log.Emsg("Config", "Error setting the fragment size parameter name");
1109 return false;
1110 }
1111 }
1112 }
1113 else if ( part == "flush" )
1114 {
1115 tmpc.m_flushRaw = cwg.GetWord();
1116 if ( ! cwg.HasLast())
1117 {
1118 m_log.Emsg("Config", "Error: pfc.flush requires a parameter.");
1119 return false;
1120 }
1121 }
1122 else if ( part == "onlyifcached" )
1123 {
1124 const char *p = 0;
1125 while ((p = cwg.GetWord()) && cwg.HasLast())
1126 {
1127 if (strcmp(p, "minsize") == 0)
1128 {
1129 std::string minBytes = cwg.GetWord();
1130 long long minBytesTop = 1024 * 1024 * 1024;
1131 if (::isalpha(*(minBytes.rbegin())))
1132 {
1133 if (XrdOuca2x::a2sz(m_log, "Error in parsing minsize value for onlyifcached parameter", minBytes.c_str(), &m_configuration.m_onlyIfCachedMinSize, 0, minBytesTop))
1134 {
1135 return false;
1136 }
1137 }
1138 else
1139 {
1140 if (XrdOuca2x::a2ll(m_log, "Error in parsing numeric minsize value for onlyifcached parameter", minBytes.c_str(),&m_configuration.m_onlyIfCachedMinSize, 0, minBytesTop))
1141 {
1142 return false;
1143 }
1144 }
1145 }
1146 if (strcmp(p, "minfrac") == 0)
1147 {
1148 std::string minFrac = cwg.GetWord();
1149 char *eP;
1150 errno = 0;
1151 double frac = strtod(minFrac.c_str(), &eP);
1152 if (errno || eP == minFrac.c_str())
1153 {
1154 m_log.Emsg("Config", "Error setting fraction for only-if-cached directive");
1155 return false;
1156 }
1157 m_configuration.m_onlyIfCachedMinFrac = frac;
1158 }
1159 else
1160 {
1161 m_log.Emsg("Config", "Error: onlyifcached stanza contains unknown directive", p);
1162 }
1163 }
1164 }
1165 else
1166 {
1167 m_log.Emsg("ConfigParameters() unmatched pfc parameter", part.c_str());
1168 return false;
1169 }
1170
1171 return true;
1172}
#define XrdOssOK
Definition XrdOss.hh:50
#define XRDOSS_mkpath
Definition XrdOss.hh:466
XrdVERSIONINFO(XrdOucGetCache, XrdPfc)
XrdSysXAttr * XrdSysXAttrActive
XrdOucCache * XrdOucGetCache(XrdSysLogger *logger, const char *config_filename, const char *parameters, XrdOucEnv *env)
Definition XrdPfc.cc:76
#define open
Definition XrdPosix.hh:76
int isNo(int dflt, const char *Msg1, const char *Msg2, const char *Msg3)
#define TRACE(act, x)
Definition XrdTrace.hh:63
bool Parse(TheLib what)
bool Plugin(XrdAccAuthorize *&piP)
Get Authorization plugin.
static XrdOfsConfigPI * New(const char *cfn, XrdOucStream *cfgP, XrdSysError *errP, XrdVersionInfo *verP=0, XrdSfsFileSystem *sfsP=0)
bool Load(int what, XrdOucEnv *envP=0)
bool Push(TheLib what, const char *plugP, const char *parmP=0)
@ theOssLib
Oss plugin.
virtual int Close(long long *retsz=0)=0
virtual int getFD()
Definition XrdOss.hh:426
virtual int Open(const char *path, int Oflag, mode_t Mode, XrdOucEnv &env)
Definition XrdOss.hh:200
virtual ssize_t Write(const void *buffer, off_t offset, size_t size)
Definition XrdOss.hh:345
long long Total
Definition XrdOssVS.hh:90
char * Get(const char *varname)
Definition XrdOucEnv.hh:69
static int Export(const char *Var, const char *Val)
Definition XrdOucEnv.cc:170
void Put(const char *varname, const char *value)
Definition XrdOucEnv.hh:85
void * Resolve(const char *symbl, int mcnt=1)
void Unload(bool dodel=false)
char * GetWord(int lowcase=0)
static int UserName(uid_t uID, char *uName, int uNsz)
static int a2i(XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1)
Definition XrdOuca2x.cc:45
static int a2sz(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1)
Definition XrdOuca2x.cc:257
static int a2ll(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1)
Definition XrdOuca2x.cc:70
static int a2tm(XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1)
Definition XrdOuca2x.cc:288
bool blocksize_str2value(const char *from, const char *str, long long &val, long long min, long long max) const
bool Config(const char *config_filename, const char *parameters, XrdOucEnv *env)
Parse configuration file.
bool prefetch_str2value(const char *from, const char *str, int &val, int min, int max) const
virtual bool ConfigDecision(const char *params)
static size_t s_maxNumAccess
virtual bool ConfigPurgePin(const char *params)
virtual int Get(const char *Aname, void *Aval, int Avsz, const char *Path, int fd=-1)=0
virtual int Set(const char *Aname, const void *Aval, int Avsz, const char *Path, int fd=-1, int isNew=0)=0
const char * trace_what_strings[]
@ CSChk_Cache
Contains parameters configurable from the xrootd config file.
Definition XrdPfc.hh:64
long long m_hdfsbsize
used with m_hdfsmode, default 128MB
Definition XrdPfc.hh:121
long long m_RamAbsAvailable
available from configuration
Definition XrdPfc.hh:108
long long m_flushCnt
nuber of unsynced blcoks on disk before flush is called
Definition XrdPfc.hh:122
long long m_cgi_max_bufferSize
max buffer size allowed in pfc.blocksize
Definition XrdPfc.hh:115
int m_accHistorySize
max number of entries in access history part of cinfo file
Definition XrdPfc.hh:100
int m_cgi_min_prefetch_max_blocks
min prefetch block count allowed in pfc.prefetch
Definition XrdPfc.hh:116
bool m_cgi_prefetch_allowed
allow cgi setting of prefetch
Definition XrdPfc.hh:119
int m_wqueue_threads
number of threads writing blocks to disk
Definition XrdPfc.hh:111
long long m_diskTotalSpace
total disk space on configured partition or oss space
Definition XrdPfc.hh:91
long long m_fileUsageMax
cache purge - files usage maximum
Definition XrdPfc.hh:96
long long m_fileUsageBaseline
cache purge - files usage baseline
Definition XrdPfc.hh:94
int m_dirStatsStoreDepth
maximum depth for statistics write out
Definition XrdPfc.hh:105
bool m_allow_xrdpfc_command
flag for enabling access to /xrdpfc-command/ functionality.
Definition XrdPfc.hh:85
long long m_diskUsageHWM
cache purge - disk usage high water mark
Definition XrdPfc.hh:93
static constexpr long long s_min_bufferSize
Definition XrdPfc.hh:131
static constexpr long long s_max_bufferSize
Definition XrdPfc.hh:132
int m_prefetch_max_blocks
default maximum number of blocks to prefetch per file
Definition XrdPfc.hh:112
bool m_cs_ChkTLS
Allow TLS.
Definition XrdPfc.hh:126
long long m_fileUsageNominal
cache purge - files usage nominal
Definition XrdPfc.hh:95
int m_cs_Chk
Checksum check.
Definition XrdPfc.hh:125
int m_purgeAgeBasedPeriod
peform cold file / uvkeep purge every this many purge cycles
Definition XrdPfc.hh:99
bool m_hdfsmode
flag for enabling block-level operation
Definition XrdPfc.hh:84
int m_purgeColdFilesAge
purge files older than this age
Definition XrdPfc.hh:98
std::string m_data_space
oss space for data files
Definition XrdPfc.hh:88
long long m_diskUsageLWM
cache purge - disk usage low water mark
Definition XrdPfc.hh:92
int m_RamKeepStdBlocks
number of standard-sized blocks kept after release
Definition XrdPfc.hh:109
long long m_bufferSize
cache block size, default 128 kB
Definition XrdPfc.hh:107
long long m_cgi_min_bufferSize
min buffer size allowed in pfc.blocksize
Definition XrdPfc.hh:114
int m_dirStatsInterval
time between resource monitor statistics dump in seconds
Definition XrdPfc.hh:104
std::string m_meta_space
oss space for metadata files (cinfo)
Definition XrdPfc.hh:89
int m_wqueue_blocks
maximum number of blocks written per write-queue loop
Definition XrdPfc.hh:110
int m_cgi_max_prefetch_max_blocks
max prefetch block count allowed in pfc.prefetch
Definition XrdPfc.hh:117
static constexpr int s_max_prefetch_max_blocks
Definition XrdPfc.hh:134
bool m_cgi_blocksize_allowed
allow cgi setting of blocksize
Definition XrdPfc.hh:118
double m_onlyIfCachedMinFrac
minimum fraction of downloaded file, used by only-if-cached CGI option
Definition XrdPfc.hh:129
time_t m_cs_UVKeep
unverified checksum cache keep
Definition XrdPfc.hh:124
int m_purgeInterval
sleep interval between cache purges
Definition XrdPfc.hh:97
long long m_onlyIfCachedMinSize
minumum size of downloaded file, used by only-if-cached CGI option
Definition XrdPfc.hh:128
std::string m_diskUsageLWM
Definition XrdPfc.hh:141
std::string m_diskUsageHWM
Definition XrdPfc.hh:142
std::string m_fileUsageBaseline
Definition XrdPfc.hh:143
std::string m_fileUsageNominal
Definition XrdPfc.hh:144
std::string m_flushRaw
Definition XrdPfc.hh:146
std::string m_fileUsageMax
Definition XrdPfc.hh:145