/******************************************************************************/ /* */ /* X r d C n s L o g F i l e . c c */ /* */ /* (c) 2008 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 #include #include "XrdCns/XrdCnsLogFile.hh" #include "XrdSys/XrdSysError.hh" #include "XrdSys/XrdSysPlatform.hh" /******************************************************************************/ /* G l o b a l s */ /******************************************************************************/ int XrdCnsLogFile::logRMax = 1024; int XrdCnsLogFile::logBMax = 1024 * sizeof(XrdCnsLogRec); namespace XrdCns { extern XrdSysError MLog; } using namespace XrdCns; /******************************************************************************/ /* D e s t r u c t o r */ /******************************************************************************/ XrdCnsLogFile::~XrdCnsLogFile() { // Wait until it is safe to delete ourselves // synSem.Wait(); // Do clean-up // if (logFD >= 0) close(logFD); if (logFN) free(logFN); if (logBuff) free(logBuff); } /******************************************************************************/ /* A d d */ /******************************************************************************/ int XrdCnsLogFile::Add(XrdCnsLogRec *lrP, int doSync) { XrdCnsLogFile *lfP = subNext; char *bP; int rc, bL; // Compute full record length // bL = lrP->setLen() + XrdCnsLogRec::MinSize; bP = lrP->Record(); // Write out record // do {do {rc = write(logFD, bP, bL);} while (rc < 0 && errno == EINTR); if (rc < 0) {MLog.Emsg("Add", errno, "add log rec to", logFN); return 0; } bP += rc; bL -= rc; } while(bL > 0); // Make sure data is on disk // if (doSync && fdatasync(logFD)) MLog.Emsg("Add", errno, "fsync log", logFN); // Notify all subscribers // while(lfP) {lfP->logSem.Post(); lfP = lfP->subNext;} // All done // return 1; } /******************************************************************************/ /* C o m m i t */ /******************************************************************************/ int XrdCnsLogFile::Commit() { static char dVal = 1; int dOffs, rc; // Commit the previous record if we have returned one // if (logOffset) {Rec.setDone(logRdr); dOffs = recOffset + XrdCnsLogRec::OffDone + logRdr; do {rc=pwrite(logFD, &dVal, 1, dOffs);} while(rc < 0 && errno == EINTR); if (rc > 0) fdatasync(logFD); else {MLog.Emsg("Commit", errno, "commit log rec in", logFN); return 0; } } return 1; } /******************************************************************************/ /* E o l */ /******************************************************************************/ int XrdCnsLogFile::Eol() { XrdCnsLogFile *lfX, *lfP = subNext; XrdCnsLogRec lRec(XrdCnsLogRec::lrEOL); int rc, bL = XrdCnsLogRec::MinSize + lRec.DLen(); char *bP = (char *)&lRec; // Write out record end of log record // do {do {rc = write(logFD, bP, bL);} while (rc < 0 && errno == EINTR); if (rc < 0) {MLog.Emsg("Eol", errno, "eol log file", logFN); break; } bP += rc; bL -= rc; } while(bL > 0); // Allow subscribers to end // while(lfP) {lfP->logSem.Post(); lfP->logWait = 0; lfX = lfP; lfP = lfP->subNext; lfX->synSem.Post(); } // All done // return 1; } /******************************************************************************/ /* g e t R e c */ /******************************************************************************/ XrdCnsLogRec *XrdCnsLogFile::getRec() { char *bP, *nP; int bL; // Wait for a record if we must wait, read it, return if it's not committed // do {if (logWait) logSem.Wait(); bP = Rec.Record(); bL = XrdCnsLogRec::MinSize; recOffset = logOffset; if (!Read(bP, bL) || !((bL = Rec.DLen()))) return 0; if (bL < XrdCnsLogRec::FixDLen) {MLog.Emsg("getRec", "Invalid record length detected in", logFN); return 0; } bP = bP + XrdCnsLogRec::MinSize; if (!Read(bP, bL)) return 0; memcpy(logNext, bP, bL); nP = logNext + XrdCnsLogRec::FixDLen + Rec.L1sz(); if (!Rec.L2sz()) *nP = '\n'; else {*nP = ' '; *(nP + Rec.L2sz() + 1) = '\n'; } logNext += bL; } while(Rec.Done(logRdr)); // Return the record or nil pointer if there is no associated file // return (Rec.L1sz() ? &Rec : 0); } /******************************************************************************/ /* O p e n */ /******************************************************************************/ int XrdCnsLogFile::Open(int allocbuff, off_t thePos) { static const int AMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // Open the file // if ((logFD = open(logFN, O_CREAT|O_RDWR, AMode)) < 0) {MLog.Emsg("Open", errno, "open", logFN); return 0;} // Set starting position if need be // if (thePos && (lseek(logFD, thePos, SEEK_SET) == (off_t)-1)) {MLog.Emsg("Open", errno, "seek into", logFN); close(logFD), logFD = -1; return 0; } // Allocate a memory buffer if so wanted // if (allocbuff) {struct stat Stat; if (fstat(logFD, &Stat)) MLog.Emsg("Open", errno, "stat", logFN); else logNext=logBuff=(char *)malloc(logWait ? logBMax : Stat.st_size); } // All done // return 1; } /******************************************************************************/ /* Private: R e a d */ /******************************************************************************/ int XrdCnsLogFile::Read(char *bP, int bL) { int rc; // Read the data // do{do {rc = pread(logFD,bP,bL,logOffset);} while(rc < 0 && errno == EINTR); if (rc < 0) {MLog.Emsg("getRec", errno, "read", logFN); return 0;} bP += rc; bL -= rc; logOffset += rc; } while(bL > 0); // All done // return 1; } /******************************************************************************/ /* S u b s c r i b e */ /******************************************************************************/ XrdCnsLogFile *XrdCnsLogFile::Subscribe(const char *Path, int cNum) { XrdCnsLogFile *lfP; int rc; // Create hard link to our log file // do {rc = link(logFN, Path);} while(rc && errno == EINTR); if (rc) {MLog.Emsg("Subscribe", errno, "create hard link", Path); return 0; } // Indicate wait is needed and that delete must wait as well // lfP = new XrdCnsLogFile(Path, cNum); lfP->logWait = 1; lfP->synSem.Wait(); // Chain in the subscriber // lfP->subNext = subNext; subNext = lfP; return lfP; } /******************************************************************************/ /* U n l i n k */ /******************************************************************************/ int XrdCnsLogFile::Unlink() { int rc; do {rc = unlink(logFN);} while(rc < 0 && errno == EINTR); if (rc < 0) MLog.Emsg("Unlink", errno, "remove log", logFN); return rc >= 0; }