/******************************************************************************/
/* */
/* X r d C n s S s i . c c */
/* */
/* (c) 2009 by the Board of Trustees of the Leland Stanford, Jr., University */
/* All Rights Reserved */
/* Produced by Andrew Hanushevsky for Stanford University under contract */
/* DE-AC02-76-SFO0515 with the Department of Energy */
/* */
/* This file is part of the XRootD software suite. */
/* */
/* XRootD is free software: you can redistribute it and/or modify it under */
/* the terms of the GNU Lesser General Public License as published by the */
/* Free Software Foundation, either version 3 of the License, or (at your */
/* option) any later version. */
/* */
/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
/* License for more details. */
/* */
/* You should have received a copy of the GNU Lesser General Public License */
/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
/* COPYING (GPL license). If not, see . */
/* */
/* The copyright holder's institutional names and contributor's names may not */
/* be used to endorse or promote products derived from this software without */
/* specific prior written permission of the institution or contributor. */
/******************************************************************************/
#include
#include
#include
#include
#include
#include
#include "XrdOuc/XrdOucHash.hh"
#include "XrdOuc/XrdOucNSWalk.hh"
#include "XrdOuc/XrdOucSxeq.hh"
#include "XrdOuc/XrdOucStream.hh"
#include "XrdOuc/XrdOucTList.hh"
#include "XrdOuc/XrdOucUtils.hh"
#include "XrdSys/XrdSysError.hh"
#include "XrdSys/XrdSysHeaders.hh"
#include "XrdCns/XrdCnsLog.hh"
#include "XrdCns/XrdCnsSsi.hh"
#include "XrdCns/XrdCnsSsiCfg.hh"
#include "XrdCns/XrdCnsSsiSay.hh"
#include "XrdCns/XrdCnsXref.hh"
#include "XrdCns/XrdCnsLogRec.hh"
/******************************************************************************/
/* L o c a l C l a s s e s */
/******************************************************************************/
struct XrdCnsSsiFRec
{
char Info[XrdCnsLogRec::FixDLen];
void Updt(const char *nInfo) {
if(nInfo == 0) return;
size_t size = strlen(nInfo);
if (size > sizeof(Info)-1)
size = sizeof(Info)-1;
memcpy(Info, nInfo, size);
Info[size] = '\0';
}
XrdCnsSsiFRec(const char *Data) {if (!Data) Data = XrdCnsLogRec::iArg;
size_t size = strlen(Data);
if (size > sizeof(Info)-1)
size = sizeof(Info)-1;
memcpy(Info, Data, size);
*Info = 'i';
Info[size] = '\0';
}
~XrdCnsSsiFRec() {}
};
struct XrdCnsSsiDRec
{
XrdOucHash *Files;
char Info[XrdCnsLogRec::FixDLen];
XrdCnsSsiDRec(const char *Data) {if (!Data) Data = XrdCnsLogRec::IArg;
size_t size = strlen(Data);
if(size > sizeof(Info)-1)
size = sizeof(Info)-1;
memcpy(Info, Data, size);
*Info = 'I';
Info[size] = '\0';
Files = new XrdOucHash;
}
~XrdCnsSsiDRec() {if (Files) delete Files;}
};
/******************************************************************************/
/* G l o b a l C o n f i g u r a t i o n O b j e c t */
/******************************************************************************/
namespace XrdCns
{
extern XrdCnsSsiCfg Config;
extern XrdSysError MLog;
extern XrdCnsSsiSay Say;
XrdOucHash *hInv;
XrdCnsXref *mountP;
XrdCnsXref *spaceP;
}
int XrdCnsSsi::nErrs = 0;
int XrdCnsSsi::nDirs = 0;
int XrdCnsSsi::nFiles= 0;
using namespace XrdCns;
/******************************************************************************/
/* E x t e r n a l I n t e r f a c e s */
/******************************************************************************/
int XrdCnsSsiApplyF(const char *Path, XrdCnsSsiFRec *fP, void *Arg)
{
static struct iovec iov[3] = {{0,sizeof(fP->Info)},{0,0},{(char *)"\n",1}};
int n, iFD = *(int *)Arg;
iov[0].iov_base = (char *)fP->Info;
iov[1].iov_base = (char *)Path; n = strlen(Path);
iov[1].iov_len = n;
XrdCnsSsi::nFiles++;
return !XrdCnsSsi::Write(iFD, iov, 3, sizeof(fP->Info)+n+1);
}
int XrdCnsSsiApplyD(const char *Path, XrdCnsSsiDRec *dP, void *Arg)
{
static struct iovec iov[3] = {{0,sizeof(dP->Info)},{0,0},{(char *)"\n",1}};
int n, iFD = *(int *)Arg;
// Return if there are no files in this directory
//
if (dP->Files->Num() <= 0) return 0;
// Write out a directory record. Terminate processing upon error
//
iov[0].iov_base = (char *)dP->Info;
iov[1].iov_base = (char *)Path; n = strlen(Path);
iov[1].iov_len = n;
XrdCnsSsi::nDirs++;
if (!XrdCnsSsi::Write(iFD, iov, 3, sizeof(dP->Info)+n+1)) return 1;
// Now index through all of the file in this directory
//
return (dP->Files->Apply(XrdCnsSsiApplyF, Arg) ? 1 : 0);
}
int XrdCnsSsiApplyM(const char *Mount, char *xP, void *Arg)
{
static char Hdr[XrdCnsLogRec::FixDLen];
static XrdCnsLogRec::Arg *aP = (XrdCnsLogRec::Arg *)Hdr;
static struct iovec iov[3] = {{Hdr,sizeof(Hdr)},{0,0},{(char *)"\n",1}};
static int doInit = 1;
int n, iFD = *(int *)Arg;
// Initialize the header (needs to be done once)
//
if (doInit)
{memset(Hdr, ' ', sizeof(Hdr));
aP->Type = XrdCnsLogRec::lrMount;
doInit = 0;
}
// Write out a directory record. Terminate processing upon error
//
aP->Mount = *xP;
iov[1].iov_base = (char *)Mount; n = strlen(Mount);
iov[1].iov_len = n;
return !XrdCnsSsi::Write(iFD, iov, 3, sizeof(Hdr)+n+1);
}
int XrdCnsSsiApplyS(const char *Space, char *xP, void *Arg)
{
static char Hdr[XrdCnsLogRec::FixDLen];
static XrdCnsLogRec::Arg *aP = (XrdCnsLogRec::Arg *)Hdr;
static struct iovec iov[3] = {{Hdr,sizeof(Hdr)},{0,0},{(char *)"\n",1}};
static int doInit = 1;
int n, iFD = *(int *)Arg;
// Initialize the header (needs to be done once
//
if (doInit)
{memset(Hdr, ' ', sizeof(Hdr));
aP->Type = XrdCnsLogRec::lrSpace;
doInit = 0;
}
// Write out a directory record. Terminate processing upon error
//
aP->Space = *xP;
iov[1].iov_base = (char *)Space; n = strlen(Space);
iov[1].iov_len = n;
return !XrdCnsSsi::Write(iFD, iov, 3, sizeof(Hdr)+n+1);
}
/******************************************************************************/
/* L i s t */
/******************************************************************************/
int XrdCnsSsi::List(const char *Host, const char *Path)
{
XrdOucStream myIF;
XrdCnsXref Mount("/",0), Space("public",0);
XrdCnsLogRec::Arg *aP = 0;
XrdOucNSWalk::NSEnt *nsP, *nsL, *nsI = 0;
struct stat Stat;
char pfxBuff[512], *pfxP = pfxBuff, *omP, *osP, *oSP;
char hBuff[256], oBuff[MAXPATHLEN+1], *ofP = oBuff;
char *tP, *lP;
int pendLog, iFD;
int dName = Config.Lopt & XrdCnsSsiCfg::Lname;
int dMount= Config.Lopt & XrdCnsSsiCfg::Lmount;
// First step is to get the files in this directory
//
nsL = XrdCnsLog::List(Path, &nsI, 1);
pendLog = (nsL != 0);
if (nsI) delete nsI;
while((nsP = nsL)) {nsL = nsL->Next; delete nsP;}
// If we have no inventory say so and return
//
if (!nsI) {Say.M("No inventory found for ", Host); return 4;}
// Open the inventory file and attach it to a stream
//
strcpy(oBuff, Path); strcat(oBuff,"/"); strcat(oBuff, XrdCnsLog::invFNz);
if ((iFD = open(oBuff, O_RDONLY)) < 0)
{Say.M("Unable to process ",oBuff,"; ",
XrdOucUtils::eText(errno, pfxBuff, sizeof(pfxBuff)));
return 1;
}
myIF.Attach(iFD, 4096);
// Preformat the output buffer
//
if (Config.Lopt & XrdCnsSsiCfg::Lhost)
{strcpy(pfxP, Host); pfxP += strlen(pfxP); *pfxP++ = ' ';}
if (Config.Lopt & XrdCnsSsiCfg::Lmode)
{omP = pfxP; pfxP += sizeof(aP->Mode); *pfxP++ = ' ';
} else omP = 0;
oSP = osP = 0;
if (Config.Lopt & XrdCnsSsiCfg::Lsize)
{if (Config.Lopt & XrdCnsSsiCfg::Lfmts) oSP = pfxP;
else osP = pfxP;
pfxP += sizeof(aP->SorT); *pfxP++ = ' ';
}
*pfxP = '\0';
// The first line should be a time stamp. If it is not us the file's ctime
//
if ((lP = myIF.GetLine()) && *lP)
{aP = (struct XrdCnsLogRec::Arg *)lP;
if (aP->Type != XrdCnsLogRec::lrTOD) fstat(iFD,&Stat);
else {Stat.st_ctime = strtol(aP->SorT,0,10) + XrdCnsLogRec::tBase;
if (*(aP->lfn) && *(aP->lfn) != ' ')
{strcpy(hBuff, aP->lfn); Host = hBuff;}
lP = myIF.GetLine();
}
} else fstat(iFD,&Stat);
tP = ctime(&Stat.st_ctime); tP[strlen(tP)-1] = '\0';
// Produce the header
//
cout <Type)
{case XrdCnsLogRec::lrMount: Mount.Add(aP->lfn, aP->Mount);
continue;
case XrdCnsLogRec::lrSpace: Space.Add(aP->lfn, aP->Space);
continue;
case XrdCnsLogRec::lrInvD: strcpy(oBuff, aP->lfn);
ofP = oBuff + strlen(oBuff);
*ofP++ = '/';
continue;
default: break;
}
if (omP) memcpy(omP, aP->Mode, sizeof(aP->Mode));
if (oSP) FSize( oSP, aP->SorT, sizeof(aP->SorT));
if (osP) memcpy(osP, aP->SorT, sizeof(aP->SorT));
if (*pfxBuff) cout <Space) <<' ';
strcpy(ofP, aP->lfn);
if (dMount) cout < " <Mount) <Next; delete nsP;}
}
private:
XrdOucNSWalk::NSEnt *nsBase;
};
XrdOucStream myIF;
XrdCnsXref Mount("/",0), Space("public",0);
XrdOucSxeq myUP(".cns_ssi_updt.", Host);
XrdOucHash myInv;
XrdCnsSsiDRec *curDir = 0;
XrdCnsSsiFRec *curFile;
XrdCnsLogRec::Arg *aP = 0;
XrdOucNSWalk::NSEnt *nsP, *nsL, *nsI = 0;
nsHelper theNS;
struct stat Stat;
char cSave, iBuff[MAXPATHLEN+1], oBuff[MAXPATHLEN+1], *lP;
int iFD, rc, TOD = 0;
// Make sure we are the only ones running here for this directory
//
if (!myUP.Serialize(XrdOucSxeq::noWait|XrdOucSxeq::Unlink))
{rc = myUP.lastError();
if (rc == EAGAIN)
Say.M(Host, " inventory is already being updated.");
else Say.M("Unable to update ", Host, " inventory; ",
XrdOucUtils::eText(rc, oBuff, sizeof(oBuff)));
return 8;
}
// Now get the files in this directory
//
nsL = XrdCnsLog::List(Path, &nsI, 1);
// If we have no inventory say so and return
//
if (!nsI) {Say.M("No inventory found for ", Host);
theNS.Set(nsL);
return 0;
}
// If no pending log files, no need to update the inventory
//
if (!nsL) {Say.V(Host," inventory is up to date.");
delete nsI;
return 0;
}
// Make sure that the full ns list is deleted
//
nsI->Next = nsL;
theNS.Set(nsI);
hInv = &myInv;
mountP = &Mount;
spaceP = &Space;
nErrs = nDirs = nFiles = 0;
// Open the inventory file and attach it to a stream
//
strcpy(iBuff, Path); strcat(iBuff,"/"); strcat(iBuff, XrdCnsLog::invFNz);
if ((iFD = open(iBuff, O_RDONLY)) < 0)
{Say.M("Unable to process ",iBuff,"; ",
XrdOucUtils::eText(errno, oBuff, sizeof(oBuff)));
return 1;
}
myIF.Attach(iFD, 4096);
// The first line should be a time stamp. If it is, throw it away.
//
if ((lP = myIF.GetLine()) && *lP)
{aP = (struct XrdCnsLogRec::Arg *)lP;
if (aP->Type == XrdCnsLogRec::lrTOD) lP = myIF.GetLine();
}
// Populate the hash table with the inventory
//
if (lP && *lP)
do {aP = (struct XrdCnsLogRec::Arg *)lP;
switch(aP->Type)
{case XrdCnsLogRec::lrMount: Mount.Add(aP->lfn, aP->Mount); break;
case XrdCnsLogRec::lrSpace: Space.Add(aP->lfn, aP->Space); break;
case XrdCnsLogRec::lrInvD: curDir = AddDir(aP->lfn, lP); break;
default: if (curDir)
{curFile = new XrdCnsSsiFRec(lP);
curDir->Files->Rep(aP->lfn, curFile);
} else Say.M("Ignoring file '", aP->lfn,
"'; missing directory in inventory.");
break;
}
} while((lP = myIF.GetLine()) && *lP);
// Done with the inventory
//
fstat(iFD, &Stat);
myIF.Close();
// Now apply each log file against the inventory
//
nsP = nsL;
while(nsP)
{if (nsP->Stat.st_ctime <= Stat.st_ctime)
Say.V("Skipping ",nsP->File,"; too old.");
else if (!(TOD = ApplyLog(nsP->Path))) return 8;
nsP = nsP->Next;
}
// Now we can open a shadow inventory file
//
strcpy(oBuff, iBuff); lP = rindex(oBuff, '/')+1;
cSave = *lP; *lP = 'i';
if ((iFD = open(oBuff, O_CREAT|O_TRUNC|O_WRONLY, AMode)) < 0)
{Say.M("Unable to create ", oBuff, "; ",
XrdOucUtils::eText(errno, iBuff, sizeof(iBuff)));
*lP = cSave;
return 8;
}
*lP = cSave;
// Create a TOD record based on the last TOD received
//
if (!Write(iFD, TOD, Host)) return 8;
// Output the space names ans mount points
//
Mount.Apply(XrdCnsSsiApplyM, (void *)&iFD);
Space.Apply(XrdCnsSsiApplyS, (void *)&iFD);
// Now output the whole name space into the inventory file
//
if (myInv.Apply(XrdCnsSsiApplyD, (void *)&iFD)) {close(iFD); return 8;}
// Close the file and rename it
//
close(iFD);
if (rename(oBuff, iBuff))
{Say.M("Unable to rename ",oBuff," to the inventory; ",
XrdOucUtils::eText(errno, iBuff, sizeof(iBuff)));
return 8;
}
// Now unlink all of the log files we processed
//
while(nsL) {unlink(nsL->Path); nsL = nsL->Next;}
// Success. All resources will be deleted upon return
//
sprintf(oBuff, "%d director%s and %d file%s updated with %d error%s.",
nDirs, (nDirs != 1 ? "ies" : "y"), nFiles, (nFiles != 1 ? "s" : ""),
nErrs, (nErrs != 1 ? "s" : ""));
Say.M(Host, " inventory with ", oBuff);
return 0;
}
/******************************************************************************/
/* A p p l y L o g */
/******************************************************************************/
int XrdCnsSsi::ApplyLog(const char *Path)
{
XrdOucStream myLog;
XrdCnsLogRec::Arg *aP = 0;
char eBuff[64], *lP;
int logFD, TOD = 0, oldErrs = nErrs;
// Open the log file
//
if ((logFD = open(Path, O_RDONLY)) < 0)
{Say.M("Unable to process ",Path,"; ",
XrdOucUtils::eText(errno, eBuff, sizeof(eBuff)));
return 0;
}
myLog.Attach(logFD, 4096);
Say.V("Processing log file ", Path);
// Update the hash table with the log file
//
while((lP = myLog.GetLine()) && *lP)
{aP = (struct XrdCnsLogRec::Arg *)lP;
if (*(aP->lfn) == '/') ApplyLogRec(lP);
else if (aP->Type == XrdCnsLogRec::lrEOL
|| aP->Type == XrdCnsLogRec::lrTOD) TOD = atoi(aP->SorT);
else {Say.V("Invalid log record: ", lP); nErrs++;}
}
// Check if we need to issue a warning
//
if (oldErrs != nErrs) Say.M("Errors encountered processing log ", Path);
// Check if we should manufacture a TOD
//
if (!TOD)
{struct stat Stat;
fstat(logFD, &Stat);
TOD = Stat.st_ctime - XrdCnsLogRec::tBase;
}
// All done, the file will be closed by the stream on exit
//
return TOD;
}
/******************************************************************************/
/* A p p l y L o g R e c */
/******************************************************************************/
void XrdCnsSsi::ApplyLogRec(char *lP)
{
XrdCnsLogRec::Arg *aP = (XrdCnsLogRec::Arg *)lP;
XrdCnsSsiDRec *theDir;
char Type = aP->Type, *lfn = aP->lfn, *fnP = 0;
// Preprosess the record to establish dir/fn relationships
//
if (aP->Type != XrdCnsLogRec::lrMkdir
&& aP->Type != XrdCnsLogRec::lrRmdir
&& aP->Type != XrdCnsLogRec::lrCreate
&& aP->Type != XrdCnsLogRec::lrMv)
{if (!(fnP = rindex(aP->lfn+1, '/')) || !(*(fnP+1))) Type = 0;
else *fnP++ = '\0';
}
// Switch on record type
//
switch (Type)
{case XrdCnsLogRec::lrClosew: AddSize(lfn, fnP, lP); break;
case XrdCnsLogRec::lrCreate: AddFile(lfn, lP); break;
case XrdCnsLogRec::lrMkdir: AddDir (lfn, lP); break;
case XrdCnsLogRec::lrRm: if ((theDir = hInv->Find(lfn)))
theDir->Files->Del(fnP);
break;
case XrdCnsLogRec::lrRmdir: hInv->Del(lfn); break;
default: if (Type == XrdCnsLogRec::lrMv && AddDel(lfn, lP)) break;
if (fnP) *(fnP -1) = '/';
Say.V("Invalid log record ", lP);
nErrs++;
}
}
/******************************************************************************/
/* Private: A d d D i r */
/******************************************************************************/
XrdCnsSsiDRec *XrdCnsSsi::AddDir(char *dP, char *lP)
{
XrdCnsSsiDRec *theDir;
// Find the directory or create one
//
if (!(theDir = hInv->Find(dP)))
{theDir = new XrdCnsSsiDRec(lP);
hInv->Add(dP, theDir);
}
return theDir;
}
/******************************************************************************/
/* Private: A d d D e l */
/******************************************************************************/
int XrdCnsSsi::AddDel(char *pPo, char *lP)
{
XrdCnsSsiDRec *newDir, *oldDir = hInv->Find(pPo);
XrdCnsSsiFRec *oldFile, *newFile;
char *diP = 0, *fnPo = 0, *fnPn = 0, *pPn = 0;
// Isolate the two lfn's
//
if (!(pPn = index(pPo, ' '))) return 0;
*pPn++ = '\0';
// First see if this is a directory rename
//
if (oldDir)
{newDir = AddDir(pPn, oldDir->Info);
delete newDir->Files;
newDir->Files = oldDir->Files;
oldDir->Files = 0;
hInv->Del(pPo);
return 1;
}
// Prepare for a file rename
//
if (!(fnPo = rindex(pPo, '/')) || !(*(fnPo+1))
|| !(fnPn = rindex(pPn, '/')) || !(*(fnPn+1))) {*(pPn-1) = ' '; return 0;}
*fnPo++ = '\0'; *fnPn++ = '\0';
// Now delete the old file
//
if ((oldDir = hInv->Find(pPo)) && (oldFile = oldDir->Files->Find(fnPo)))
{newFile = new XrdCnsSsiFRec(oldFile->Info);
diP = oldDir->Info;
oldDir->Files->Del(fnPo);
} else newFile = new XrdCnsSsiFRec(0);
// Add the new file
//
newDir = AddDir(pPn, diP);
newDir->Files->Add(fnPn, newFile);
// All done
//
return 1;
}
/******************************************************************************/
/* Private: A d d F i l e */
/******************************************************************************/
XrdCnsSsiFRec *XrdCnsSsi::AddFile(char *lfn, char *lP)
{
XrdCnsLogRec::Arg *aP = (XrdCnsLogRec::Arg *)lP;
XrdCnsSsiDRec *theDir;
XrdCnsSsiFRec *theFile;
char *fP, *mP, *sP, rDir[] = {'/', 0};
// Extract out the directory, file name, space name and mount point, if any
//
if ((sP = index(lfn, ' '))) *sP++ = '\0';
if (!(fP = rindex(lfn+1, '/')) || !(*(fP+1)))
{if (!fP && *lfn == '/' && *(lfn+1)) {fP = lfn+1; lfn = rDir;}
else {if (sP) *(sP-1) = ' ';
Say.V("Invalid log record ", lP); nErrs++;
return 0;
}
} else *fP++ = '\0';
if (sP)
{if (!(mP = index(sP, ' '))) aP->Mount = mountP->Default();
else {*mP++ = '\0'; aP->Mount = mountP->Add(mP);}
if (*sP) aP->Space = spaceP->Add(sP);
else aP->Space = spaceP->Default();
} else { aP->Mount = mountP->Default();
aP->Space = spaceP->Default();
}
// Add the file if it does not exist
//
theDir = AddDir(lfn, 0);
if ((theFile = theDir->Files->Find(fP))) theFile->Updt(lP);
else {theFile = new XrdCnsSsiFRec(lP);
theDir->Files->Add(fP, theFile);
}
return theFile;
}
/******************************************************************************/
XrdCnsSsiFRec *XrdCnsSsi::AddFile(char *dP, char *fP, char *lP)
{
XrdCnsSsiDRec *theDir = AddDir(dP, 0);
XrdCnsSsiFRec *theFile;
// Add the file if it does not exist
//
if ((theFile = theDir->Files->Find(fP))) theFile->Updt(lP);
else {theFile = new XrdCnsSsiFRec(lP);
theDir->Files->Add(fP, theFile);
}
return theFile;
}
/******************************************************************************/
/* Private: A d d S i z e */
/******************************************************************************/
void XrdCnsSsi::AddSize(char *dP, char *fP, char *lP)
{
XrdCnsLogRec::Arg *nP = (XrdCnsLogRec::Arg *)lP;
XrdCnsSsiDRec *theDir = hInv->Find(dP);
XrdCnsSsiFRec *theFile;
// Find directory
//
if (!theDir || !(theFile = theDir->Files->Find(fP)))
theFile = AddFile(dP, fP, 0);
XrdCnsLogRec::Arg *aP = (XrdCnsLogRec::Arg *)(theFile->Info);
strncpy(aP->SorT, nP->SorT, sizeof(aP->SorT));
}
/******************************************************************************/
/* Private: F S i z e */
/******************************************************************************/
void XrdCnsSsi::FSize(char *oP, char *iP, int bsz)
{
static const long long Kval = 1024LL;
static const long long Mval = 1024LL*1024LL;
static const long long Gval = 1024LL*1024LL*1024LL;
static const long long Tval = 1024LL*1024LL*1024LL*1024LL;
long long val;
char buff[32], sName = ' ';
int n, resid;
// Convert the number
//
val = strtoll(iP, 0, 10);
// Get correct scaling
//
if (val < 1024) {memcpy(oP, iP, bsz); return;}
if (val < Mval) {val = val*10/Kval; sName = 'K';}
else if (val < Gval) {val = val*10/Mval; sName = 'M';}
else if (val < Tval) {val = val*10/Gval; sName = 'G';}
else {val = val*10/Tval; sName = 'T';}
resid = val%10LL; val = val/10LL;
// Format it
//
n = sprintf(buff,"%lld.%d%c", val, resid, sName);
memset(oP, ' ', bsz);
memcpy(oP+(bsz-n), buff, n);
}
/******************************************************************************/
/* W r i t e */
/******************************************************************************/
int XrdCnsSsi::Write(int xFD, char *bP, int bL)
{
char eBuff[64];
int rc;
do {do {rc = write(xFD, bP, bL);} while (rc < 0 && errno == EINTR);
if (rc < 0) {Say.M("Unable to update inventory; ",
XrdOucUtils::eText(errno, eBuff, sizeof(eBuff)));
return 0;
}
bP += rc; bL -= rc;
} while(bL > 0);
return 1;
}
/******************************************************************************/
int XrdCnsSsi::Write(int xFD, struct iovec *iov, int n, int Bytes)
{
char eBuff[64], *Buff;
int rc, i, Blen;
do {rc = writev(xFD, iov, n);} while(rc < 0 && errno == EINTR);
if (rc < 0) {Say.M("Unable to update inventory; ",
XrdOucUtils::eText(errno, eBuff, sizeof(eBuff)));
return 0;
}
if (rc == Bytes) return 1;
for (i = 0; i < n; i++)
{if (Bytes >= (int)iov[i].iov_len) Bytes -= iov[i].iov_len;
else {Buff = Bytes + (char *)iov[i].iov_base;
Blen = iov[i].iov_len - Bytes;
if (!Write(xFD, Buff, Blen)) return 0;
Bytes = 0;
}
}
return 1;
}
/******************************************************************************/
int XrdCnsSsi::Write(int iFD, int TOD, const char *Host)
{
XrdCnsLogRec::Arg tRec;
char buff[32];
int n;
memset(&tRec, ' ', XrdCnsLogRec::FixDLen);
tRec.Type = XrdCnsLogRec::lrTOD;
memset(tRec.Mode, '0', sizeof(tRec.Mode));
n = sprintf(buff, "%d", TOD);
memcpy(tRec.SorT+sizeof(tRec.SorT)-n, buff, n);
strcpy(tRec.lfn, Host);
n = strlen(tRec.lfn);
tRec.lfn[n] = '\n';
return Write(iFD, (char *)&tRec, XrdCnsLogRec::FixDLen+n+1);
}