35 #include <sys/param.h>
36 #include <sys/types.h>
68 void isEmpty(
struct stat *dStat,
const char *dPath,
const char *lkFN);
71 {expDirTime = dExp; lowDirTime = 0; numRMD = numEMD = 0;}
90 static const char *What = (
Config.Test ?
"Zorch " :
"Purged ");
93 char Parent[MAXPATHLEN+1], *Slash;
99 if (dStat->st_mtime > expDirTime)
100 {
if (!lowDirTime || lowDirTime > dStat->st_mtime)
101 lowDirTime = dStat->st_mtime;
108 strcpy(Parent, dPath);
110 if (Parent[n-1] ==
'/') Parent[--n] =
'\0';
111 if ((Slash = rindex(Parent,
'/')))
113 if (
stat(Parent, &pStat)) Slash = 0;
120 {times.actime = pStat.st_atime;
121 times.modtime = pStat.st_mtime;
122 utime(Parent, ×);
133 localtime_r(&(dStat->st_mtime), &tNow);
134 sprintf(sbuff,
"%02d%02d%02d %02d:%02d:%02d ",
135 tNow.tm_year-100, tNow.tm_mon+1, tNow.tm_mday,
136 tNow.tm_hour, tNow.tm_min, tNow.tm_sec);
137 Say.
Say(What,
"empty dir ", sbuff, dPath);
155 int XrdFrmPurge::Left2Do = 0;
157 time_t XrdFrmPurge::lastReset = 0;
158 time_t XrdFrmPurge::nextReset = 0;
166 strncpy(SName, snp,
sizeof(SName)-1); SName[
sizeof(SName)-1] =
'\0';
177 SNlen = strlen(SName);
178 memset(DeferQ, 0,
sizeof(DeferQ));
196 if ((baseFile->
Link))
199 if (!(psP = Find(snBuff))) psP =
Default;
204 if (!(psP->Enabled)) {
delete sP;
return;}
209 if ((Why = psP->Eligible(sP, xTime)))
217 if (xTime >= psP->Hold) psP->FSTab.
Add(sP);
218 else psP->Defer(sP, xTime);
232 for (n = DeferQsz-1; n >= 0 && !DeferQ[n]; n--) {}
234 if (time(0) - DeferT[n] > Hold)
return 0;
235 fP = DeferQ[n]; DeferQ[n] = 0; DeferT[n] = 0;
254 void XrdFrmPurge::Clear()
261 for (n = 0; n < DeferQsz; n++)
262 while ((fP = DeferQ[n])) {DeferQ[n] = fP->
Next;
delete fP;}
263 memset(DeferT, 0,
sizeof(DeferT));
271 numFiles = 0; prgFiles = 0; purgBytes = 0;
281 int n = xTime/DeferQsz;
285 if (n >= DeferQsz) n = DeferQsz-1;
286 if (!DeferQ[n] || aTime < DeferT[n]) DeferT[n] = aTime;
287 sP->
Next = DeferQ[n];
301 char buff[1024], minfsp[32], maxfsp[32];
305 Say.
Say(
"=====> ",
"Purge configuration:");
310 {
Say.
Say(
"=====> ",
"Scanning ", (vP->
Val?
"r/w: ":
"r/o: "), vP->
Name);
312 while(tP) {
Say.
Say(
"=====> ",
"Excluded ", tP->
text); tP = tP->
next;}
318 if (
Config.dirHold < 0) strcpy(buff,
"forever");
319 else sprintf(buff,
"%d",
Config.dirHold);
320 Say.
Say(
"=====> ",
"Directory hold: ", buff);
329 isExt = spP->Ext ?
" polprog" :
"";
330 sprintf(buff,
"policy %s min %s max %s free; hold: %d%s",
331 spP->SName, minfsp, maxfsp, spP->Hold, isExt);
332 }
else sprintf(buff,
"policy %s nopurge", spP->SName);
342 const char *XrdFrmPurge::Eligible(
XrdFrmFileset *sP, time_t &xTime,
int hTime)
345 time_t aTime, mTime, nowTime = time(0);
349 aTime = baseFile->
Stat.st_atime;
350 mTime = baseFile->
Stat.st_mtime;
354 xTime =
static_cast<int>(nowTime - aTime);
355 if (hTime && xTime <= hTime)
return "is in hold";
359 if (sP->
failFile())
return "has fail file";
366 return "not migrated";
375 return "is perm pinned";
381 return "is time pinned";
387 return "is pin deferred";
401 while(spP && strcmp(snp, spP->SName)) spP = spP->Next;
422 int setIt, rc, haveExt = 0;
428 while(vP && strcmp(spP->SName, vP->
Name)) vP = vP->
Next;
429 if (!vP && strcmp(
"public", spP->SName))
430 {
Say.
Emsg(
"Init",
"Purge policy", spP->SName,
431 "deleted; space not defined.");
432 if (ppP) ppP->Next = spP->Next;
433 else First = spP->Next;
434 xP = spP; spP = spP->Next;
delete xP;
435 }
else {ppP = spP; spP = spP->Next;}
444 {
while(tP && strcmp(tP->
text, spP->SName)) tP = tP->
next;
448 {
if (minV) spP->minFSpace = spP->maxFSpace = minV;
449 if (hVal >= 0) {spP->Hold = hVal; spP->Hold2x = hVal*2;}
450 if (spP->minFSpace && spP->Hold >= 0)
451 {spP->Enabled = 1; haveExt |= spP->Ext;}
459 spP = First; ppP = 0;
463 {
Say.
Emsg(
"Init", rc,
"calculate space for", spP->SName);
464 if (ppP) ppP->Next = spP->Next;
465 else First = spP->Next;
466 xP = spP; spP = spP->Next;
delete xP;
continue;
468 spP->totlSpace = vsInfo.
Total;
469 spP->spaceTLen = sprintf(xBuff,
"%lld", vsInfo.
Total);
470 spP->spaceTotl = strdup(xBuff);
471 spP->pmaxSpace = vsInfo.
Large;
472 spP->spaceTLep = sprintf(xBuff,
"%lld", vsInfo.
Large);
473 spP->spaceTotP = strdup(xBuff);
474 if (spP->minFSpace < 0)
475 {spP->minFSpace = vsInfo.
Total *
XRDABS(spP->minFSpace) / 100LL;
476 spP->maxFSpace = vsInfo.
Total *
XRDABS(spP->maxFSpace) / 100LL;
477 }
else if (vsInfo.
Total < spP->minFSpace
478 || vsInfo.
Total < spP->maxFSpace)
479 Say.
Emsg(
"Init",
"Warning: ", spP->SName,
" min/max free "
480 "space policy exceeds total available!");
481 ppP = spP; spP = spP->Next;
487 if (!(
Default = Find(
"public")))
488 {
Say.
Emsg(
"Init",
"Unable to start purge; no public policy found.");
494 if (
Config.pProg && haveExt)
499 {memcpy(
Config.pVec, pVec,
sizeof(pVec));
500 Config.pVecNum =
sizeof(pVec);
517 int XrdFrmPurge::LowOnSpace()
529 else {psP->freeSpace = VSInfo.
Free;
530 psP->contSpace = VSInfo.
LFree;
531 psP->usedSpace = VSInfo.
Usage;
532 if (psP->freeSpace >= psP->minFSpace) psP->Stop = 1;
533 else {Left2Do++; psP->Stop = 0;}
535 }
else psP->Stop = 1;
541 if (!Left2Do)
return 0;
555 if (eNow >= nextReset) {lastReset = eNow; nextReset = 0; Scan();}
564 long long maxV,
int hVal,
int xVal)
570 if (!(psP = Find(sname))) First = psP =
new XrdFrmPurge(sname, First);
574 psP->minFSpace = minV;
575 psP->maxFSpace = maxV;
577 psP->Hold2x = hVal*2;
593 {
Say.
Emsg(
"Purge",
"Purge cycle skipped; all policies met.");
599 Say.
Emsg(
"Purge",
"Purge cycle started.");
605 while(psP && Left2Do)
606 {
if (!(psP->Stop) && (psP->Stop = psP->PurgeFile())) Left2Do--;
614 Say.
Emsg(
"Purge",
"Purge cycle ended.");
621 int XrdFrmPurge::PurgeFile()
625 const char *fn, *Why;
627 int rc, FilePurged = 0;
631 do{
if (!(fP = FSTab.
Oldest()) && !(fP = Advance()))
632 {time_t nextScan = time(0)+Hold;
633 if (!nextReset || nextScan < nextReset) nextReset = nextScan;
637 if (fP->
Refresh() && !(Why = Eligible(fP, xTime, Hold))
638 && (!Ext || !(Why = XPolOK(fP))))
640 rc = (
Config.Test ? 0 : PurgeFile(fP, fn));
641 if (!rc) {prgFiles++; FilePurged = 1;
644 if (
Config.Verbose) Track(fP);
646 }
else {
DEBUG(
"Purge " <<SName <<
": keeping " <<fP->
basePath() <<
"; " <<Why);}
648 }
while(!FilePurged && !Stop);
652 return freeSpace >= maxFSpace || Stop;
657 int XrdFrmPurge::PurgeFile(
XrdFrmFileset *fP,
const char *pFN)
659 static const char *Me =
"frm_purged:0.0@localhost";
662 char mBuff[MAXPATHLEN+1024];
663 int n, rc, isLFN = 0;
671 if (!(isLFN =
Config.LogicalPath(pFN, mBuff,
sizeof(mBuff))))
676 if (
Config.cmsPath)
Config.cmsPath->Gone(mBuff, !isLFN);
684 snprintf(mBuff+n,
sizeof(mBuff)-n,
685 "\n&tod=%lld&sz=%lld&at=%lld&ct=%lld&mt=%lld&fn=%c",
686 static_cast<long long>(time(0)),
687 static_cast<long long>(bfP->
Stat.st_size),
688 static_cast<long long>(bfP->
Stat.st_atime),
689 static_cast<long long>(bfP->
Stat.st_ctime),
690 static_cast<long long>(bfP->
Stat.st_mtime),
691 (isLFN ?
'l' :
'p'));
704 void XrdFrmPurge::Scan()
708 static time_t lastHP = time(0), nextDP = 0, nowT = time(0);
717 int needLF,
ec = 0, Bad = 0, aFiles = 0, bFiles = 0;
725 if (
Config.dirHold < 0 || nowT < nextDP) {cbP = 0; Extra = 0;}
726 else {nextDP = nowT +
Config.dirHold;
729 Extra =
"and empty directory";
734 VMSG(
"Scan",
"Name space", Extra,
"scan started. . .");
740 while((sP = fP->
Get(
ec,1)))
742 if (sP->
Screen(needLF)) Add(sP);
743 else {
delete sP; bFiles++;}
747 }
while((vP = vP->
Next));
755 sprintf(buff,
"%d of %d empty dir%s removed", purgeDir.
numRMD,
757 VMSG(
"Scan",
"Empty directory space scan ended;", buff);
762 sprintf(buff,
"%d file%s with %d error%s", aFiles, (aFiles != 1 ?
"s":
""),
763 bFiles, (bFiles != 1 ?
"s":
""));
764 VMSG(
"Scan",
"Name space scan ended;", buff);
768 if (Bad)
Say.
Emsg(
"Scan",
"Errors encountered while scanning for "
776 void XrdFrmPurge::Stats(
int Final)
779 long long pVal, xBytes, zBytes;
780 const char *xWhat, *nWhat, *zWhat;
781 char fBuff[64], uBuff[80], sBuff[512], xBuff[64], zBuff[64];
788 if (!(xsP->Enabled))
continue;
789 if (xsP->usedSpace >= 0)
790 {
if (
Final) xsP->usedSpace -= xsP->purgBytes;
792 pVal = xsP->usedSpace*100/xsP->totlSpace;
793 sprintf(uBuff,
"used %s (%lld%%) ", fBuff, pVal);
794 }
else *uBuff =
'\0';
796 pVal = xsP->freeSpace*100/xsP->totlSpace;
798 {xBytes = xsP->purgBytes; xWhat =
"freed";
799 if ((zBytes = xsP->maxFSpace - xsP->freeSpace) > 0)
802 }
else {*zBuff =
'\0'; zWhat =
"need met";}
803 nFiles = xsP->prgFiles; nWhat =
"prgd";
805 xBytes = (xsP->freeSpace < xsP->minFSpace
806 ? xsP->maxFSpace - xsP->freeSpace : 0);
807 nFiles = xsP->FSTab.
Count();
808 xWhat =
"needed"; nWhat =
"idle"; *zBuff =
'\0'; zWhat =
"";
811 sprintf(sBuff,
" %sfree %s (%lld%%) %d files %d %s; %s %s %s%s",
812 uBuff,fBuff,pVal,xsP->numFiles,nFiles,nWhat,
813 xBuff,xWhat,zBuff,zWhat);
814 Say.
Say(
"++++++ ", xsP->SName, sBuff);
825 const char *What = (
Config.Test ?
"Zorch " :
"Purged ");
826 char sbuff[128], fszbf[16];
832 fszbf,
sizeof(fszbf));
836 localtime_r(&(fP->
Stat.st_atime), &tNow);
837 sprintf(sbuff,
" %8s %02d%02d%02d %02d:%02d:%02d ", fszbf,
838 tNow.tm_year-100, tNow.tm_mon+1, tNow.tm_mday,
839 tNow.tm_hour, tNow.tm_min, tNow.tm_sec);
850 static char neg1[] = {
'-',
'1',
'\0'};
852 char *Data[
sizeof(
Config.pVec)*2+2];
853 int Dlen[
sizeof(
Config.pVec)*2+2];
854 char atBuff[32], ctBuff[32], mtBuff[32], fsBuff[32], spBuff[32], usBuff[32];
860 for (i = 0; i <
Config.pVecNum; i++)
864 Dlen[k] = sprintf(atBuff,
"%lld",
865 static_cast<long long>(fP->
Stat.st_atime));
869 Dlen[k] = sprintf(ctBuff,
"%lld",
870 static_cast<long long>(fP->
Stat.st_ctime));
873 Data[k] = fP->
File; Dlen[k] = strlen(fP->
File);
877 Dlen[k] = sprintf(fsBuff,
"%lld",
878 static_cast<long long>(fP->
Stat.st_size));
882 Dlen[k] = sprintf(spBuff,
"%lld", freeSpace);
886 Dlen[k] = sprintf(mtBuff,
"%lld",
887 static_cast<long long>(fP->
Stat.st_mtime));
891 Dlen[k] = strlen(Data[k]);
894 Data[k] = SName; Dlen[k] = SNlen;
897 Data[k] = spaceTotl; Dlen[k] = spaceTLen;
900 if (usedSpace < 0) {Data[k] = neg1; Dlen[k]=2;}
901 else {Dlen[k] = sprintf(usBuff,
"%lld",
902 usedSpace - purgBytes);
908 Data[++k] = (
char *)
" "; Dlen[k] = 1; k++;
913 Data[k-1] = (
char *)
"\n"; Data[k] = 0; Dlen[k] = 0;
917 if (PolProg->
Feed((
const char **)Data, Dlen) || !(Resp=PolStream->
GetLine()))
918 {Stop = 1;
return "external policy failed";}
922 if (*Resp ==
'y')
return 0;
923 if (*Resp ==
'n')
return "external policy reject";
925 return "external policy stop";
int stat(const char *path, struct stat *buf)
const kXR_char XROOTD_MON_MAPPURG
static const char pinKeep
static const char pinPerm
static const char pinIdle
static void Rmd(const char *Path, int islfn=0)
static void Rm(const char *Path, int islfn=0)
static const int CompressD
static const int NoAutoDel
XrdFrmFileset * Get(int &rc, int noBase=0)
static const int Recursive
int Refresh(int isMig=0, int doLock=1)
XrdOucXAttr< XrdFrcXAttrCpy > cpyInfo
XrdOucXAttr< XrdFrcXAttrPin > pinInfo
XrdOucNSWalk::NSEnt * baseFile()
XrdOucNSWalk::NSEnt * failFile()
static kXR_unt32 Map(char code, const char *uname, const char *path)
void isEmpty(struct stat *dStat, const char *dPath, const char *lkFN)
static int Init(XrdOucTList *sP=0, long long minV=-1, int hVal=-1)
XrdFrmPurge(const char *snp, XrdFrmPurge *spp=0)
static XrdFrmPurge * Policy(const char *sname)
int Add(XrdFrmFileset *fsp)
static int getCname(const char *path, char *Cache, char *lbuf=0, int lbsz=0)
static const int minSNbsz
virtual int Remdir(const char *path, int Opts=0, XrdOucEnv *envP=0)=0
virtual int StatVS(XrdOssVSInfo *vsP, const char *sname=0, int updt=0)
virtual int Unlink(const char *path, int Opts=0, XrdOucEnv *envP=0)=0
XrdOucStream * getStream() const
int Feed(const char *data[], const int dlen[])
int Setup(const char *prog, XrdSysError *errP=0, int(*Proc)(XrdOucStream *, char **, int)=0)
static int fmtBytes(long long val, char *buff, int bsz)
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
void Say(const char *text1, const char *text2=0, const char *txt3=0, const char *text4=0, const char *text5=0, const char *txt6=0)