Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

DDSFileHandler.cxx

Go to the documentation of this file.
00001 
00002 //                                                                           //
00003 // DDSFileHandler                                                            //
00004 //                                                                           //
00005 // Package: DDS (Data Dispatcher System).                                    //
00006 //                                                                           //
00007 // S. Kasahara 5/2001                                                        //
00008 //                                                                           //
00009 // Purpose: Interacts with system to determine next available file for a     //
00010 //          given DDS::EDataSource.  Will construct and return the full file //
00011 //          path name of the next file when requested.                       //
00012 //                                                                           //
00013 // The fullfilepathname of the current file for a given data source is       //
00014 // determined from the following:                                            //
00015 // The enumerated data source given to the DDSFileHandler at construction    //
00016 // (DDS::kDaq or DDS::kDcs) is used to read the externally set               //
00017 // environment variable DDS_DAQ_DATA_DIR or DDS_DCS_DATA_DIR which contains  //
00018 // the path to the directory of the source data files.                       //
00019 // If the user has subscribed to the Dispatcher with keepup mode             //
00020 // DDS::kFileKeepUp, then it is expected that the user maintains a symbolic  //
00021 // link file "currentfile" within this source directory that points to the   //
00022 // most recent data file generated by that datasource.                       //
00023 // Thus the full path of the current file generated by the data source is:   //
00024 // $DDS_DAQ_DATA_DIR/currentfile or $DDS_DCS_DATA_DIR/currentfile            //
00025 // If the user has subscribed to the Dispatcher with keepup mode             //
00026 // DDS::kAll, then all files in the directory ending with the extension      //
00027 // ".root" will be served starting with the currentfile if defined or with   //
00028 // the first file in the directory if not defined.                           //
00029 //                                                                           //
00030 // If the user wishes to use the rootd server to serve *open* files from a   //
00031 // disk on a machine other than the one on which the DDS runs (this should   //
00032 // only be of concern for the detector sites), the path to the source        //
00033 // directory should be expanded to include the "root:" preface and the host  //
00034 // name on which the rootd server actually runs.  An example:                //
00035 // setenv DDS_DAQ_DATA_DIR root://urheim.hep.umn.edu/data/minos/daq         //
00036 // or                                                                        //
00037 // setenv DDS_DAQ_DATA_DIR root://urheim.hep.umn.edu/~minos/daq              //
00038 // where the format is:                                                      //
00039 //                         root://<host name>/<path name>                    //
00040 // The username&password of the remote machine on which the rootd server runs//
00041 // must be specified in a .netrc file located in the user's home directory,  //
00042 // unless rootd has been set up for anonymous usage.                         //
00043 // The format for a line in the .netrc file is:                              //
00044 //            machine <host name> login <username> password <password>       //
00045 //            e.g.                                                           //
00046 //            machine urheim.hep.umn.edu login myusername password mypassword//
00047 // If the username&password is not found in the .netrc file, then anonymous  //
00048 // login is attempted with username:anonymous password:username@hostname     //
00049 //                                                                           //
00050 // Note that even when rootd is being used to serve the files to the         //
00051 // ddschildserver, the ddschildserver must still be able to "see" the        //
00052 // directory containing the data using a file sharing system so that it can  //
00053 // the symbolic link "currentfile" and directory contents with unix system   //
00054 // functions.   If the local directory path differs from the <path name>     //
00055 // specified in the env DDS_DAQ_DATA_DIR as root://<host name>/<path name>   //
00056 // then the local directory path should be specified in a separate env       //
00057 // variable DAQ_DATA_DIR.  Confused yet?                                     //
00058 //                                                                           //
00060 
00061 #include <algorithm>  // vector sort
00062 #include <sys/stat.h>  
00063 #include <unistd.h>    
00064 #include <pwd.h>
00065 
00066 #include "TAuthenticate.h"
00067 #include "TString.h"
00068 #include "TSystem.h"
00069 #include "TUrl.h"
00070 
00071 #include "MessageService/MsgService.h"
00072 #include "Dispatcher/DDSFileHandler.h"
00073 
00074 std::ostream& operator << (std::ostream& ms, DDSFileHandler* dfh)
00075                                  { return dfh->Print(ms); } 
00076 
00077 ClassImp(DDSFileHandler)
00078 
00079 // Definition of static data members
00080 // *********************************
00081 
00082 CVSID("$Id: DDSFileHandler.cxx,v 1.26 2008/11/18 21:16:14 rhatcher Exp $");
00083  
00084 // Definition of methods (alphabetical order)
00085 // ******************************************
00086 
00087 Int_t DDSFileHandler::BuildFileIndex() {
00088   //
00089   // Purpose: Build file index of files in source directory.  Files are
00090   //          assumed to have subscript of form ".root".  
00091   //
00092   // Argument: none.
00093   // 
00094   // Return: Int_t  number of files in index
00095   //
00096   // Contact: S. Kasahara
00097   //
00098 
00099 
00100   fFileIndex.clear(); fFileIndexItr = fFileIndex.end(); // clear previous index
00101 
00102   if ( !IsValid() ) {
00103     MSG("DDS",Msg::kWarning) 
00104     << "BuildFileIndex called on InValid FileHandler." << endl;
00105     return 0;
00106   }
00107 
00108   // Open source directory
00109   void *dir = gSystem -> OpenDirectory(fLocalDataSourceDir.c_str());
00110   if (!dir) {
00111     MSG("DDS",Msg::kWarning) << "Error in open of file source directory " 
00112      << fLocalDataSourceDir << ".\nCannot build file index." << endl;
00113     return 0;
00114   }
00115   
00116   // update last modification time of source directory
00117   struct stat keystat;
00118   if ( stat(fLocalDataSourceDir.c_str(),&keystat) >= 0 ) {
00119     fSourceDirModTime = keystat.st_ctime;
00120   }
00121 
00122   const char* file;
00123   string filename;
00124   while ((file = gSystem -> GetDirEntry(dir)) ) {
00125     filename = file;
00126     // Determine if file has required extension
00127     if ( filename.length() > 5 
00128       && filename.find(".root") == filename.length()-5 ) {
00129       fFileIndex.push_back(fDataSourceDir+"/"+filename);
00130     }
00131   }
00132   // sort the directory contents in alphanumeric order
00133   std::sort(fFileIndex.begin(),fFileIndex.end());
00134 
00135   gSystem -> FreeDirectory(dir);
00136 
00137   fFileIndexItr = fFileIndex.begin();
00138 
00139   MsgStream& ms = MSGSTREAM("DDS",Msg::kVerbose);
00140   ms << "CS_" << gSystem->GetPid() << ": "<< "Built new FileIndex: " << endl;
00141   for (FileIndexConstItr citr  = fFileIndex.begin(); 
00142                          citr != fFileIndex.end(); citr++) {
00143     ms << "    " << *citr << endl; 
00144   }
00145   return fFileIndex.size();
00146 
00147 }
00148 
00149 std::string DDSFileHandler::BuildFullFilePathName(std::string filename) {
00150   //
00151   // Purpose: Build full file path name from file name.
00152   //
00153   // Argument: string    filename of interest.
00154   //
00155   // Return: fullfilepathname of file.
00156   //
00157   // Contact: S. Kasahara
00158   //
00159   // Notes: If the filename contains an absolute
00160   //        path specification beginning with "/", then nothing is added
00161   //        to the filename to create the fullfilepathname.  If the filename
00162   //        indicates a relative path, this method will prepend the data source
00163   //        directory path to the input filename if not already present. 
00164   //  
00165 
00166   std::string fullfilepathname = filename;
00167   unsigned int namelen = filename.length();
00168   if ( filename.find(fDataSourceDir) >= namelen ) {
00169     // Check if filename is a relative path and prepend data source directory
00170     if( filename.find("/") != 0 ) {
00171       fullfilepathname = fDataSourceDir + "/" + fullfilepathname;
00172     }
00173   }  
00174 
00175   // Check to see if fullfilepathname is symbolic link, and if so
00176   // expand to link target name
00177   const Int_t bufsize = 512;
00178   char buffer[bufsize];
00179 
00180   Int_t len = readlink(fullfilepathname.c_str(),buffer,bufsize);
00181   if ( len >= 0 ) {
00182     buffer[len] = '\0';  // null terminate string
00183     fullfilepathname = buffer;
00184     fullfilepathname = this -> BuildFullFilePathName(fullfilepathname);
00185   }
00186   return fullfilepathname;
00187 
00188 }
00189 
00190 
00191 DDSFileHandler::DDSFileHandler(DDS::EDataSource datasource,bool offLine) : 
00192 fDataSource(datasource),fDataSourceEnv(""),fDataSourceDir(""),
00193 fLocalDataSourceDir(""),fSymLink(""),fSymLinkTarget(""),fSymLinkModTime(0),
00194 fSourceDirModTime(0),fLastServedFile(""),fValid(true),fFileIndexItr(0),
00195 fOffLine(offLine) {
00196   // 
00197   // Purpose: Default constructor for DDSFileHandler class.  
00198   //  
00199   // Arguments: datasource   enumerated type specifies source of data. 
00200   //            offline      true => use symbolic link 'offlinefile'
00201   //                         instead of 'currentfile'. default = false.
00202   // Return: n/a.
00203   //
00204   // Contact: S. Kasahara
00205   //
00206   // Notes: Use IsValid() to determine if DDSFileHandler was initialized
00207   //        properly.
00208   //
00209 
00210 
00211   // Get name of directory containing data for requested datasource
00212   string dataSourceString = DDS::AsString(datasource);
00213   fDataSourceEnv = "DDS_"+dataSourceString+"_DATA_DIR";
00214   const char* sourcedir = getenv(fDataSourceEnv.c_str());
00215   if ( sourcedir == NULL ) {
00216     MSG("DDS",Msg::kWarning) << "Environment variable " 
00217                              << fDataSourceEnv << " undefined." << endl;
00218     fValid = false;
00219     return;
00220   } 
00221   else {
00222     fDataSourceDir = sourcedir;
00223   }
00224 
00225   // Determine if fDataSourceDir points to a remote host with rootd server
00226   if ( fDataSourceDir.find("root:")==0 || fDataSourceDir.find("roots:")==0 ) {
00227     TUrl url(fDataSourceDir.c_str());
00228 
00229     // check for existence of DAQ_DATA_DIR or DCS_DATA_DIR
00230     // indicating a local directory path different than that contained
00231     // in DDS_DAQ_DATA_DIR or DDS_DCS_DATA_DIR.
00232     string localDataSourceEnv = dataSourceString+"_DATA_DIR";
00233     const char* localSourceDir = getenv(localDataSourceEnv.c_str());
00234     if (localSourceDir != NULL) {
00235       fLocalDataSourceDir = localSourceDir;
00236     }
00237     else { 
00238       // Parse the source directory to determine the "local path name", used
00239       // to find the symbolic link
00240       string symlink = url.GetFile();
00241       fLocalDataSourceDir = symlink.substr(1,symlink.length()-1);
00242     }
00243     // Check to make sure that user has defined username&password for remote
00244     // host in .netrc file, if not will assume anonymous access
00245     TAuthenticate auth(0,url.GetHost(),url.GetProtocol(),0);
00246 
00247     TString tuser;
00248     TString tpasswd;
00249     if ( !auth.CheckNetrc(tuser,tpasswd) ) {
00250       MSG("DDS",Msg::kInfo) << "CS_" << gSystem->GetPid() << ": "
00251         << "No remote login data in ~.netrc. Assuming anonymous rootd usage."
00252         << endl;
00253       TAuthenticate::SetGlobalUser("anonymous");
00254       passwd* pwstruct = getpwuid(getuid());
00255       const char* username = 0;
00256       if (pwstruct) username = pwstruct -> pw_name;
00257       string password;
00258       if (username != NULL) 
00259         password = string(username) +"@" + string(gSystem->HostName());
00260       else
00261         password = "minos@" + string(gSystem->HostName());
00262       TAuthenticate::SetGlobalPasswd(password.c_str());
00263     }
00264 
00265   }
00266   else {
00267     fLocalDataSourceDir = fDataSourceDir; 
00268   }
00269   if ( fOffLine ) 
00270     fSymLink = fLocalDataSourceDir + "/offlinefile";
00271   else
00272     fSymLink = fLocalDataSourceDir + "/currentfile";
00273   
00274 }
00275 
00276 DDSFileHandler::~DDSFileHandler() {
00277   // Purpose: Destructor.
00278   //
00279   // Argument: n/a.
00280   //
00281   // Return: n/a.
00282   //
00283   // Contact: S. Kasahara
00284   //
00285 
00286 }
00287 
00288 std::string DDSFileHandler::GetCurrentFileName() const {
00289   //
00290   // Purpose: Retrieves full path name of file pointed to by fFileIndexItr.  
00291   //
00292   // Argument: none.
00293   // 
00294   // Return: std::string  full file path name of current file.
00295   //
00296   // Contact: S. Kasahara
00297   //
00298 
00299   std::string currentFileName;
00300   if (fFileIndexItr != fFileIndex.end() ) currentFileName = *fFileIndexItr;
00301   return currentFileName;
00302 
00303 }
00304 
00305 std::string DDSFileHandler::GetSymLinkTargetName() {
00306   //
00307   // Purpose: Retrieves full path name of file pointed to by fSymLink.
00308   //          (either "currentfile" or "offlinefile" depending on fOffLine
00309   //           bool).  
00310   //
00311   // Argument: none.
00312   // 
00313   // Return: std::string  full file path name of target of fSymLink.
00314   //
00315   // Contact: S. Kasahara
00316   //
00317 
00318   if ( this -> IsModifiedSymLink() ) {
00319     fSymLinkTarget = "";
00320     // update last modification time of key file
00321     struct stat keystat;
00322     if ( lstat(fSymLink.c_str(),&keystat) >= 0 ) {
00323       fSymLinkModTime = keystat.st_ctime;
00324       const Int_t bufsize = 512;
00325       char buffer[bufsize];
00326       Int_t len = readlink(fSymLink.c_str(),buffer,bufsize);
00327       if ( len >= 0 ) {
00328         buffer[len] = '\0';  // null terminate string
00329         if (buffer[0] == '/')   // absolute path
00330             fSymLinkTarget = buffer;
00331         else
00332             fSymLinkTarget = fDataSourceDir + "/" + buffer;
00333       }
00334       else {
00335         MSG("DDS",Msg::kWarning) << "CS_" << gSystem -> GetPid()
00336                                  << ": Error in reading symbolic link file:\n"
00337                                  << fSymLink << "." << endl;
00338       }
00339     }
00340     else {
00341       MSG("DDS",Msg::kWarning) << "CS_" << gSystem->GetPid()
00342                                << ": Error in reading symbolic link file:\n"
00343                                << fSymLink << " modification time." << endl;
00344     }
00345   }  
00346   
00347   return fSymLinkTarget;
00348   
00349 }
00350 
00351 std::string DDSFileHandler::GoToFile(std::string filename) {
00352   //
00353   // Purpose: Advance file iterator to point to the file indicated by
00354   //          fullFilePathName.  
00355   //
00356   // Argument: std::string  file name to advance to.  If file name does
00357   //                        not include the directory source path, this
00358   //                        information will be prepended to the file name.
00359   //                        If filename is null, advances to first file in 
00360   //                        directory. 
00361   //
00362   // Return: name of file corresponding to position index iterator was left at.
00363   //
00364   // Contact: S. Kasahara
00365   //
00366   // Notes: If specified file is not found, index iterator is left to
00367   //        point at 1 just beyond the missing file in the file index.
00368   //        If specified filename is null, index iterator is left to
00369   //        point at beginning of file index.
00370   //
00371 
00372   if ( this -> IsModifiedDirectory() ) this -> BuildFileIndex();
00373 
00374   std::string fullFilePathName = this -> BuildFullFilePathName(filename);
00375 
00376   if ( filename.empty() ) {
00377     fFileIndexItr = fFileIndex.begin();
00378   }
00379   else {
00380     for ( fFileIndexItr=fFileIndex.begin(); fFileIndexItr!=fFileIndex.end(); 
00381           fFileIndexItr++ ) {
00382       if ( (*fFileIndexItr) >= fullFilePathName ) {
00383         break;
00384       } 
00385     }
00386   }
00387 
00388   std::string nextFile = this -> GetCurrentFileName();
00389   if ( !nextFile.empty() ) {
00390     fLastServedFile = nextFile;
00391   }
00392 
00393   return nextFile;
00394 
00395 }
00396 
00397 std::string DDSFileHandler::GoToNextFile() {
00398   //
00399   // Purpose: Move current file iterator to point to the next file
00400   //          in source directory and return fullfilepathname of this file.
00401   //
00402   // Argument: none.
00403   // 
00404   // Return: std::string  full file path name of next file.
00405   //
00406   // Contact: S. Kasahara
00407   //
00408   // Notes:  Files are presumed to have names ending in  ".root".
00409   //         Files are in alphanumeric order.  If first call,
00410   //         the start of the file index is positioned at file pointed
00411   //         to by the symbolic link "currentfile", or if that symbolic
00412   //         link is not present, to the first file in the directory. 
00413   //
00414 
00415   std::string nextFileName = "";
00416   std::string currentFileName = this -> GetCurrentFileName();
00417 
00418   bool isNewBuild = false;
00419   if ( this -> IsModifiedDirectory() ) {
00420     this -> GetSymLinkTargetName();
00421     this -> BuildFileIndex();
00422     isNewBuild = true;
00423   }
00424  
00425   if ( fFileIndex.empty() ) {
00426     fFileIndexItr = fFileIndex.end();
00427     return "";
00428   }
00429 
00430   if ( fLastServedFile.empty() ) {
00431     // Start from currentfile symbolic link if available
00432     // Else first file in directory
00433     nextFileName = fSymLinkTarget;
00434     nextFileName = this -> GoToFile(nextFileName); 
00435   }
00436   else {
00437     // Move to next file in file index
00438     if ( !isNewBuild ) {
00439       if ( fFileIndexItr != fFileIndex.end() ) fFileIndexItr++;
00440       nextFileName = this -> GetCurrentFileName();
00441     }
00442     else {
00443       // if index has changed, must find position of currentfile before advance
00444       nextFileName =  this -> GoToFile(fLastServedFile);
00445       if ( strcmp(nextFileName.c_str(),fLastServedFile.c_str()) ) {
00446         MSG("DDS",Msg::kWarning) << "CS_" <<  gSystem -> GetPid()
00447         << ": The data source directory\n" 
00448         << "    no longer contains the last served file " << currentFileName
00449         << "\n    indicating a potential gap in the files served." 
00450         << " Will set index to next file beyond missing file in directory." 
00451         << endl;
00452       }
00453       else if ( fFileIndexItr != fFileIndex.end() ) {
00454         fFileIndexItr++;
00455         nextFileName = this->GetCurrentFileName();
00456       }
00457     }
00458   }            
00459 
00460   if ( !nextFileName.empty() ) {
00461     fLastServedFile = nextFileName;
00462   }
00463 
00464   return nextFileName;
00465   
00466 }
00467 
00468 std::string DDSFileHandler::GoToSymLinkFile() {
00469   //
00470   // Purpose: Move current file iterator to point to the file pointed
00471   //          to by the symbolic link 'currentfile' or 'offlinefile'.
00472   //
00473   // Argument: none.
00474   // 
00475   // Return: std::string  fullfilepathname of file pointed to by
00476   //                      symbolic link.
00477   //
00478   // Contact: S. Kasahara
00479   //
00480 
00481   std::string symLinkTarget = this -> GetSymLinkTargetName();
00482   if ( !symLinkTarget.empty() ) {
00483     // Attempt to move to it
00484     this -> GoToFile(symLinkTarget);
00485   }
00486 
00487   std::string nextFile = this -> GetCurrentFileName();
00488   if (!nextFile.empty()) {
00489     fLastServedFile = nextFile;
00490   }
00491 
00492   return nextFile;
00493   
00494 }
00495 
00496 bool DDSFileHandler::IsModifiedDirectory() const {
00497   //
00498   // Purpose: Determine if directory containing files has been modified.
00499   //
00500   // Argument: none.
00501   // 
00502   // Return: none.
00503   //
00504   // Contact: S. Kasahara
00505   //
00506 
00507   if ( IsValid() ) { 
00508   // Check last mod time of source directory and compare it to stored mod time
00509     struct stat keystat;
00510     if (stat(fLocalDataSourceDir.c_str(),&keystat) >= 0) {
00511       if (fSourceDirModTime != keystat.st_ctime) {
00512         return true;
00513       }
00514     }
00515   }
00516 
00517   return false;
00518 
00519 }
00520 
00521 bool DDSFileHandler::IsModifiedSymLink() const {
00522   //
00523   // Purpose: Determine if symbolic link has been modified since last
00524   //          call to GetSymLinkFileName().
00525   //
00526   // Argument: none.
00527   // 
00528   // Return: none.
00529   //
00530   // Contact: S. Kasahara
00531   //
00532 
00533   if ( IsValid() ) { 
00534   // Check last mod time of key file and compare it to stored mod time
00535     struct stat keystat;
00536     if (lstat(fSymLink.c_str(),&keystat) >= 0) {
00537       if (fSymLinkModTime != keystat.st_ctime) {
00538         return true;
00539       }
00540     }
00541   }
00542 
00543   return false;
00544 
00545 }
00546 
00547 bool DDSFileHandler::NewFileAvailable() {
00548   //
00549   // Purpose: Compare GetSymLinkFileName() to GetCurrentFileName() and
00550   //          return true if different.
00551   //
00552   // Argument: none.
00553   // 
00554   // Return: true if current file is different than symbolic link file.
00555   //
00556   // Contact: S. Kasahara
00557   //
00558 
00559   if ( IsValid() ) { 
00560     this -> GetSymLinkTargetName();
00561     if ( !fSymLinkTarget.empty() ) {
00562       if ( strcmp( (this->GetSymLinkTargetName()).c_str(),
00563                    (this->GetCurrentFileName()).c_str() ) ) {
00564         return true;
00565       }
00566     }
00567   }
00568 
00569   return false;
00570 
00571 }
00572 
00573 std::ostream& DDSFileHandler::Print(std::ostream& ms) const {
00574   //
00575   // Purpose: Print DDSFileHandler status on std::ostream.
00576   //
00577   // Argument: ms  std::ostream to print on.
00578   //
00579   // Return: std::ostream reference.
00580   //
00581   // Contact: S. Kasahara
00582   //
00583 
00584   if ( IsValid() ) {
00585     ms << "DDSFileHandler is serving files for data source " 
00586        << DDS::AsString(fDataSource) << " from directory:\n  "
00587        << fDataSourceDir << "." << endl;
00588     ms << "The symbolic link:\n  " << fSymLink << "\nshould point to "
00589        << "the most recent " << DDS::AsString(fDataSource)        
00590        << " file." << endl;
00591     if ( !fSymLinkTarget.empty() ) {
00592       ms << "This symbolic link file pointed to " 
00593          << fSymLinkTarget << " on last check." << endl;
00594     }
00595     else {
00596       ms << "This symbolic link file did not point to a file when last checked." 
00597          << endl;
00598     }
00599     ms << "There are " << fFileIndex.size() << " entries in the file index."
00600        << endl;
00601     for (unsigned int i = 0; i < fFileIndex.size(); i++) {
00602       ms << "  " << i+1 << ")" << fFileIndex[i] << endl;
00603     } 
00604   }
00605   else {
00606     ms << "DDSFileHandler was unable to access the environment variable "
00607        << fDataSourceEnv << "." << endl;
00608   }
00609 
00610   return ms;
00611 
00612 }
00613 
00614 
00615 
00616 
00617 
00618 
00619 
00620 

Generated on Mon Feb 15 11:06:36 2010 for loon by  doxygen 1.3.9.1