/******************************************************************************/ /* */ /* X r d P o s i x O b j e c t . h h */ /* */ /* (c) 2013 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 "XrdPosix/XrdPosixObject.hh" #include "XrdPosix/XrdPosixTrace.hh" #include "XrdSys/XrdSysHeaders.hh" #include "XrdSys/XrdSysTimer.hh" /******************************************************************************/ /* S t a t i c M e m b e r s */ /******************************************************************************/ XrdSysMutex XrdPosixObject::fdMutex; XrdPosixObject **XrdPosixObject::myFiles = 0; int XrdPosixObject::highFD = -1; int XrdPosixObject::lastFD = -1; int XrdPosixObject::baseFD = 0; int XrdPosixObject::freeFD = 0; int XrdPosixObject::posxFD = 0; int XrdPosixObject::devNull = -1; /******************************************************************************/ /* A s s i g n F D */ /******************************************************************************/ bool XrdPosixObject::AssignFD(bool isStream) { XrdSysMutexHelper fdHelper(fdMutex); int fd; // Obtain a new filedscriptor from the system. Use the fd to track the object. // Streams are not supported for virtual file descriptors. // if (baseFD) { if (isStream) return 0; for (fd = freeFD; fd < posxFD && myFiles[fd]; fd++) {} if (fd >= posxFD) return 0; freeFD = fd+1; } else { do{if ((fd = dup(devNull)) < 0) return false; if (fd >= lastFD || (isStream && fd > 255)) {close(fd); return 0;} if (!myFiles[fd]) break; DMSG("AssignFD", "FD " < highFD) highFD = fd; fdNum = fd + baseFD; // All done. // return true; } /******************************************************************************/ /* D i r */ /******************************************************************************/ XrdPosixDir *XrdPosixObject::Dir(int fd, bool glk) { XrdPosixDir *dP; XrdPosixObject *oP; int waitCount = 0; bool haveLock; // Validate the fildes // do{if (fd >= lastFD || fd < baseFD) {errno = EBADF; return (XrdPosixDir *)0;} // Obtain the file object, if any // fdMutex.Lock(); if (!(oP = myFiles[fd - baseFD]) || !(oP->Who(&dP))) {fdMutex.UnLock(); errno = EBADF; return (XrdPosixDir *)0;} // Attempt to lock the object in the appropriate mode. If we fail, then we need // to retry this after dropping the global lock. We pause a bit to let the // current lock holder a chance to unlock the lock. We only do this a limited // amount of time (1 minute) so that we don't get stuck here forever. // if (glk) haveLock = oP->objMutex.CondWriteLock(); else haveLock = oP->objMutex.CondReadLock(); if (!haveLock) {fdMutex.UnLock(); waitCount++; if (waitCount > 120) break; XrdSysTimer::Wait(500); // We wait 500 milliseconds continue; } // If the global lock is to be held, then release the object lock as this // is a call to destroy the object and there is no need for the local lock. // if (glk) oP->UnLock(); else fdMutex.UnLock(); return dP; } while(1); // If we get here then we timedout waiting for the object lock // errno = ETIMEDOUT; return (XrdPosixDir *)0; } /******************************************************************************/ /* F i l e */ /******************************************************************************/ XrdPosixFile *XrdPosixObject::File(int fd, bool glk) { XrdPosixFile *fP; XrdPosixObject *oP; int waitCount = 0; bool haveLock; // Validate the fildes // do{if (fd >= lastFD || fd < baseFD) {errno = EBADF; return (XrdPosixFile *)0;} // Obtain the file object, if any // fdMutex.Lock(); if (!(oP = myFiles[fd - baseFD]) || !(oP->Who(&fP))) {fdMutex.UnLock(); errno = EBADF; return (XrdPosixFile *)0;} // Attempt to lock the object in the appropriate mode. If we fail, then we need // to retry this after dropping the global lock. We pause a bit to let the // current lock holder a chance to unlock the lock. We only do this a limited // amount of time (1 minute) so that we don't get stuck here forever. // if (glk) haveLock = oP->objMutex.CondWriteLock(); else haveLock = oP->objMutex.CondReadLock(); if (!haveLock) {fdMutex.UnLock(); waitCount++; if (waitCount > 120) break; XrdSysTimer::Wait(500); // We wait 500 milliseconds continue; } // If the global lock is to be held, then release the object lock as this // is a call to destroy the object and there is no need for the local lock. // if (glk) oP->UnLock(); else fdMutex.UnLock(); return fP; } while(1); // If we get here then we timedout waiting for the object lock // errno = ETIMEDOUT; return (XrdPosixFile *)0; } /******************************************************************************/ /* I n i t */ /******************************************************************************/ int XrdPosixObject::Init(int fdnum) { static const int maxFD = 1048576; struct rlimit rlim; int isize, limfd; // Initialize the /dev/null file descriptors, bail if we cannot // devNull = open("/dev/null", O_RDWR, 0744); if (devNull < 0) return -1; // Obtain the file descriptor limit but be careful of infinity // if (getrlimit(RLIMIT_NOFILE, &rlim)) limfd = maxFD; else {if (rlim.rlim_max == RLIM_INFINITY || (int)rlim.rlim_max > maxFD) {rlim.rlim_cur = maxFD; setrlimit(RLIMIT_NOFILE, &rlim); } else { if (rlim.rlim_cur != rlim.rlim_max) {rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_NOFILE, &rlim); } } limfd = static_cast(rlim.rlim_cur); } // Compute size of table. if the passed fdnum is negative then the caller does // not want us to shadow fd's (ther caller promises to be honest). Otherwise, // the actual fdnum limit will be based on the current limit. // if (fdnum < 0) {posxFD = fdnum = -fdnum; baseFD = limfd;} else fdnum = limfd; isize = fdnum * sizeof(XrdPosixFile *); // Allocate the table for fd-type pointers // if (!(myFiles = (XrdPosixObject **)malloc(isize))) lastFD = -1; else {memset((void *)myFiles, 0, isize); lastFD = fdnum+baseFD;} // All done // return baseFD; } /******************************************************************************/ /* R e l e a s e */ /******************************************************************************/ void XrdPosixObject::Release(XrdPosixObject *oP, bool needlk) { // Get the lock if need be // if (needlk) fdMutex.Lock(); // Remove the object from the table // if (baseFD) {int myFD = oP->fdNum - baseFD; if (myFD < freeFD) freeFD = myFD; myFiles[myFD] = 0; } else { myFiles[oP->fdNum] = 0; close(oP->fdNum); } // Zorch the object fd and release the global lock // oP->fdNum = -1; fdMutex.UnLock(); } /******************************************************************************/ /* R e l e a s e D i r */ /******************************************************************************/ XrdPosixDir *XrdPosixObject::ReleaseDir(int fd) { XrdPosixDir *dP; // Find the directory object // if (!(dP = Dir(fd, true))) return (XrdPosixDir *)0; // Release it and return the underlying object // Release((XrdPosixObject *)dP, false); return dP; } /******************************************************************************/ /* R e l e a s e F i l e */ /******************************************************************************/ XrdPosixFile *XrdPosixObject::ReleaseFile(int fd) { XrdPosixFile *fP; // Find the file object // if (!(fP = File(fd, true))) return (XrdPosixFile *)0; // Release it and return the underlying object // Release((XrdPosixObject *)fP, false); return fP; } /******************************************************************************/ /* S h u t d o w n */ /******************************************************************************/ void XrdPosixObject::Shutdown() { XrdPosixObject *oP; int i; // Destory all files and static data // fdMutex.Lock(); if (myFiles) {for (i = 0; i <= highFD; i++) if ((oP = myFiles[i])) {myFiles[i] = 0; if (oP->fdNum >= 0) close(oP->fdNum); oP->fdNum = -1; delete oP; }; free(myFiles); myFiles = 0; } fdMutex.UnLock(); }