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

JobCEnv.cxx

Go to the documentation of this file.
00001 
00002 // $Id: JobCEnv.cxx,v 1.42 2007/08/17 01:31:02 rhatcher Exp $
00003 //
00004 // A collection of data from the job environment - global options and
00005 // resources.
00006 //
00007 // messier@huhepl.harvard.edu
00009 #include "JobControl/JobCEnv.h"
00010 extern "C" {
00011 #ifdef MACOSX
00012   #include <unistd.h>
00013 #else
00014   #include <getopt.h>
00015 #endif
00016 #ifndef GETOPTDONE // Some getopt.h's have this, some don't...
00017 #define GETOPTDONE (-1)
00018 #endif
00019 #include <sys/types.h> // opendir, closedir
00020 #include <dirent.h>    // opendir, closedir
00021 }
00022 #include <cstdlib>
00023 #include <cassert>
00024 #include <cstring>
00025 #include <csignal>
00026 #include "TEnv.h"
00027 #include "TROOT.h"
00028 #include "TSystem.h"
00029 #include "JobControl/JobCRootEnv.h"
00030 #include "JobControl/JobCleaner.h"
00031 #include "JobControl/JobCFloatXImp.h"
00032 #include "JobControl/IsArgMacroFile.h"
00033 #include "MessageService/MsgService.h"
00034 #include "MinosObjectMap/MomNavigator.h"
00035 
00036 CVSID("$Id: JobCEnv.cxx,v 1.42 2007/08/17 01:31:02 rhatcher Exp $");
00037 
00038 JobCEnv* JobCEnv::fInstance = 0;
00039 bool     gsSIGHUP = false; // Have we recieved a SIGHUP signal?
00040 bool     gsSIGUSR1 = false; // Have we recieved a SIGUSR1 signal?
00041 
00042 //......................................................................
00043 
00044 static void gsSIGHUPhandler(int /* s */) { gsSIGHUP = true; }
00045 static void gsSIGUSR1handler(int /* s */) { gsSIGUSR1 = true; }
00046 
00047 int JobCEnvSetenv(const char *name, const char *value) {
00048   // A transportable "setenv" that works on both Linux and IRIX
00049   //    int setenv(const char *name, const char *value, int overwrite);  
00050   // found in Linux's <stdlib.h> is NOT POSIX standard
00051   // This is just like TUnixSystem::Setenv, but avoids
00052   // entanglement with ROOT at this stage
00053 
00054   char *s = new char [strlen(name)+strlen(value)+2];  // space for = & \0
00055   sprintf(s,"%s=%s",name,value);
00056   int success = ::putenv(s); // putenv is more standard than setenv
00057   // passed string becomes part of the environment, so altering
00058   // the string changes the environment -- DON'T delete 's'
00059   return success;
00060 }
00061 
00062 //......................................................................
00063 
00064 JobCEnv::JobCEnv() :
00065   fArgc(0),
00066   fArgv(0), 
00067   fModuleHelpList(0),
00068   fDfltOutFile(""),
00069   fTimeStart(time(0)),
00070   fTimeLimit(-1.0),
00071   fRecordLimit(-1),
00072   fIsBatch(false), 
00073   fRootEnv(0)
00074 {
00075 //========================================================================
00076 // Purpose: Create the default job environment 
00077 //========================================================================
00078 }
00079 
00080 //......................................................................
00081 
00082 JobCEnv::JobCEnv(int argc, char** argv) :
00083   fArgc(argc), 
00084   fArgv(argv), 
00085   fModuleHelpList(0),
00086   fDfltOutFile(""),
00087   fTimeStart(time(0)),
00088   fTimeLimit(-1.0),
00089   fRecordLimit(-1),
00090   fIsBatch(false),
00091   fRootEnv(0)
00092 { 
00093 //========================================================================
00094 // Purpose: Create the job environment given command line options
00095 //========================================================================
00096   this->ProcessCommandLine(argc,argv);
00097 
00098   // Only set up the root environment after command line has been parsed
00099   // Don't forget delete in d'tor
00100   fRootEnv = new JobCRootEnv(argc,argv);
00101   assert(fRootEnv);
00102 
00103   // Set the system signal handlers
00104   this->SetSignalHandlers();
00105 }
00106 
00107 //......................................................................
00108 
00109 JobCEnv::~JobCEnv() 
00110 { 
00111 //======================================================================
00112 // Purpose: Clean up after JobCEnv...
00113 //======================================================================
00114   if (fRootEnv) {
00115     delete fRootEnv; fRootEnv = 0;
00116   }
00117 }
00118 
00119 //......................................................................
00120 
00121 const char* JobCEnv::GetModuleHelpList() const 
00122 {
00123   return fModuleHelpList;
00124 }
00125 
00126 //......................................................................
00127 
00128 int JobCEnv::GetArgc() const { return fArgc; }
00129 
00130 //......................................................................
00131 
00132 char* const* JobCEnv::GetArgv() const { return fArgv; }
00133 
00134 //......................................................................
00135 
00136 const char* JobCEnv::GetArgv(int i) const 
00137 { 
00138 //======================================================================
00139 // Purpose: Return the ith command line argument
00140 //======================================================================
00141   assert(i>=0 && i<fArgc);
00142   return fArgv[i]; 
00143 }
00144 
00145 //......................................................................
00146 
00147 const char* JobCEnv::GetMacroFile(unsigned int i) const
00148 {
00149 //======================================================================
00150 // Purpose: Return the ith macro file specified on the command line
00151 //======================================================================
00152   //always true: assert(i>=0);
00153   if (i<this->fMacroFileList.size()) return fMacroFileList[i].c_str();
00154   else                               return 0;
00155 }
00156 
00157 //......................................................................
00158 
00159 const char* JobCEnv::GetFileName(int i) const 
00160 {
00161 //======================================================================
00162 // Purpose: Return the ith data file name listed on the command line
00163 //======================================================================
00164   // Compute index into argv array
00165   assert(i>=0 && i<this->GetNfile());
00166   return fDataFileList[i].c_str();
00167 }
00168 
00169 //......................................................................
00170 
00171 int JobCEnv::GetNfile() const 
00172 {
00173 //======================================================================
00174 // Purpose: Return the number of data file names specified on the 
00175 //          command line
00176 //======================================================================
00177   return fDataFileList.size();
00178 }
00179 
00180 //......................................................................
00181 
00182 const char* JobCEnv::GetDefaultOutputFileName() const 
00183 {
00184 //======================================================================
00185 // Return the name of the default output file. Value set from command
00186 // line.
00187 //======================================================================
00188   return fDfltOutFile.c_str();
00189 }
00190 
00191 //......................................................................
00192 
00193 bool JobCEnv::HaveInaccessibleFile() const
00194 {
00195     if (!fRootEnv) return false;
00196     return fRootEnv->HaveInaccessibleFile();
00197 }
00198 
00199 //......................................................................
00200 
00201 void JobCEnv::SetTimeLimit(const char* s) 
00202 {
00203 //======================================================================
00204 // Set the time limit for the job. Expected format is <float> <unit>
00205 // for example:
00206 //   '10.5 minutes'
00207 //   '2.5 hours'
00208 //   '90 seconds'
00209 //======================================================================
00210   int i;                  // Loop variable
00211   float t;                // Numerical value of time limit
00212   char unit[256];         // Units that the time is given in
00213   double fac;             // Factor to apply to t to convert to seconds
00214   // My best guess all all the possible units people could use...
00215   const char* second[] = {"s","second","seconds","sec", "secs", 0};
00216   const char* minute[] = {"m","minute","minutes","min", "mins", 0};
00217   const char* hour[]   = {"h","hour","hours","hr","hrs",        0};
00218   const char* day[]    = {"d","day","days",                     0};
00219   const char* week[]   = {"w","week","weeks","wk","wks",        0};
00220   const char* year[]   = {"y","year","years","yr","yrs",        0};
00221 
00222   // Read the value and unit of the time limit string
00223   sscanf(s,"%f %s",&t,unit);
00224 
00225   // Sort which units the time limit is given in
00226   fac = 0.0;
00227   for (i=0; second[i]!=0; ++i) {
00228     if (strcasecmp(unit,second[i])==0) fac = 1.0;
00229   }
00230   for (i=0; minute[i]!=0; ++i) {
00231     if (strcasecmp(unit,minute[i])==0) fac = 60.0;
00232   }
00233   for (i=0; hour[i]!=0; ++i) {
00234     if (strcasecmp(unit,hour[i])==0)   fac = 60.0*60.0;
00235   }
00236   for (i=0; day[i]!=0; ++i) {
00237     if (strcasecmp(unit,day[i])==0)    fac = 60.0*60.0*24.0;
00238   }
00239   for (i=0; week[i]!=0; ++i) {
00240     if (strcasecmp(unit,week[i])==0)   fac = 60.0*60.0*24.0*7.0;
00241   }
00242   for (i=0; year[i]!=0; ++i) {
00243     if (strcasecmp(unit,year[i])==0)   fac=60.0*60.0*24.0*365.25;
00244   }
00245   if (fac==0.0 && strlen(unit)>0) {
00246     MSG("JobC",Msg::kWarning) <<
00247       "Time unit '"<<unit<<"' is not recognized.\n" <<
00248       "Try 's', 'm', 'h', 'd', 'w', or 'y'\n" <<
00249       "Assuming unit given is seconds.\n";
00250     fac = 1.0;
00251   }
00252   
00253   // Set the time limit
00254   fTimeLimit = t*fac;
00255 }
00256 
00257 //......................................................................
00258 
00259 void JobCEnv::SetSignalHandlers()
00260 {
00261 //======================================================================
00262 // Set signal handlers.
00263 //======================================================================
00264   signal(SIGHUP,gsSIGHUPhandler);    // End job "gracefully"
00265   signal(SIGUSR1,gsSIGUSR1handler);    // Emit status msg on signal
00266 
00267   bool activateFPE = true;
00268 
00269   if(gEnv) {
00270     if(strcasecmp(gEnv->GetValue("Loon.fpe","on"),"off")==0) activateFPE=false;
00271   } 
00272   static JobCFloatXImp floatX(activateFPE); // Trap floating point exceptions
00273 
00274 }
00275 
00276 //......................................................................
00277 
00278 bool JobCEnv::CheckRecordLimit(int n) const
00279 {
00280 //======================================================================
00281 // Check if the number of records n is within the set limit. Return
00282 // true if the limit has been exceeded. False if it has not.
00283 //======================================================================
00284   if (fRecordLimit<0)  return false; // No limit set
00285   if (n>=fRecordLimit) return true;
00286   return false;
00287 }
00288 
00289 bool JobCEnv::CheckTimeLimit(int* tsec) const
00290 {
00291 //======================================================================
00292 // Check if the time limit for the job has been exceeded. Return true
00293 // if the limit for the job has been exceeded. False if it has not.
00294 //======================================================================
00295   if (fTimeLimit<0.0) return false; // No limit set
00296 
00297   time_t t = time(0);
00298   *tsec = t; // Return time used in seconds
00299   if (difftime(t,fTimeStart) >= fTimeLimit) return true;
00300   
00301   return false;
00302 }
00303 
00304 //......................................................................
00305 
00306 bool JobCEnv::ContinueRun(int n) const
00307 {
00308 //======================================================================
00309 // Check if there is any information from the environment that signals
00310 // whether a run should stop. 
00311 //
00312 // Inputs: n = number of records processed
00313 // Returns: true=keep running, false=stop run
00314 //======================================================================
00315   int t;
00316   if (this->CheckRecordLimit(n) == true) {
00317     MSG("JobC",Msg::kWarning) << 
00318       "Record limit exceeded at "<<n<<" records." << endl;
00319     return false;
00320   }
00321   if (this->CheckTimeLimit(&t) == true) {
00322     MSG("JobC",Msg::kWarning) << 
00323       "Time limit exceeded at "<<t<<" seconds." << endl;
00324     return false;
00325   }
00326   if (gsSIGHUP) {
00327     MSG("JobC",Msg::kWarning) << 
00328       "Process received SIGHUP.\n" << endl;
00329     return false;
00330   }
00331   if (gsSIGUSR1) {
00332     int run, snarl;
00333     MsgService::Instance()->GetCurrentRunSnarl(run,snarl);
00334     cout << flush;  // just so things don't get intermixed
00335     cerr << "JobCEnv: Process " << gSystem->GetPid() 
00336          << " received SIGUSR1 after "
00337          << n << " record sets," 
00338          << endl
00339          <<"  last processed run " << run << " snarl " << snarl 
00340          << flush << endl;
00341     ProcInfo_t pi;
00342     gSystem->GetProcInfo(&pi);
00343     cerr << "  CPU: user " << pi.fCpuUser << "s, sys " 
00344          << pi.fCpuSys << "s, "
00345          << " Mem: res " << pi.fMemResident 
00346          << "k, virtual " << pi.fMemVirtual << "k "
00347          << flush << endl;
00348 
00349     gsSIGUSR1 = false;  // we've emitted the msg, clear signal
00350   }
00351   return true; // Keep going...
00352 }
00353 
00354 //......................................................................
00355 
00356 bool JobCEnv::IsBatch() const { return fIsBatch; }
00357 
00358 //......................................................................
00359 
00360 JobCEnv& JobCEnv::Instance() 
00361 {
00362   if (fInstance) return *fInstance;
00363   return JobCEnv::Instance(0,0);
00364 }
00365 
00366 //......................................................................
00367 
00368 JobCEnv& JobCEnv::Instance(int argc, char** argv)
00369 {
00370   if (fInstance) return *fInstance;
00371 
00372   // Helper class to handle delete
00373   static JobCEnv::Cleaner c;
00374   c.ClassIsUsed();
00375 
00376   fInstance = new JobCEnv(argc, argv);
00377   return *fInstance;
00378 }
00379 
00380 //......................................................................
00381 
00382 int JobCEnv::RunRootApp() 
00383 {
00384 //======================================================================
00385 // Purpose: Transfer control to the ROOT application
00386 //======================================================================
00387   if (fRootEnv) {
00388     int r = fRootEnv->RunTheApp();
00389     // Ensure that JobC objects get deleted. Root is sloopy about this...
00390     JobCleaner::Instance().Reap();
00391     return r;
00392   }
00393   return 0;
00394 }
00395 
00396 //......................................................................
00397 
00398 void JobCEnv::ProcessCommandLine(int argc, char *const *argv)
00399 {
00400 //======================================================================
00401 // Purpose: Parse command line options
00402 // Inputs: Standard "argc" and "argv" from main()
00403 //======================================================================
00404   int c;
00405   
00406 #ifndef MACOSX
00407   optind = 0; // getopt.h: Reset getopt to start of arguments
00408 #else
00409   optind = 1; // skip 0th argument ("loon") for MACOSX
00410 #endif
00411 
00412 #ifdef IRIX6
00413   getoptreset(); // needed by IRIX to reset getopt
00414 #endif
00415   while ((c = getopt(argc, argv, "bs:nqlt:r:x:hH:d:u:p:o:v:")) != GETOPTDONE) {
00416     MSG("JobC",Msg::kDebug) << "Processing job command-line option argument: " << c << endl;
00417     switch (c) {
00418     case 'b': break; // ROOT batch option
00419     case 's': break; // ROOT's hidden -splash option...
00420     case 'q': break; // ROOT quit option
00421     case 'n': break; // ROOT logon/logoff macro option
00422     case 'l': break; // ROOT no splash option
00423     case 't': this->SetTimeLimit(optarg);  break; // Set time limit
00424     case 'r': fRecordLimit = atoi(optarg); break; // Set record limit
00425     case 'x': // x is to "execute" a JobControl macro
00426       this->AddMacroFile(optarg); // optarg from getopt.h
00427       break;
00428     case 'h':
00429       MSG("JobC",Msg::kInfo) 
00430         << " usage: " << argv[0] 
00431         << " -bnq -H<modules> -x<file>"
00432         << " -d<url> -u<user> -p<passwd> -t'<time>' -r[n]"
00433         << " -o<filename> <filenames...>\n"
00434         << "  -b: Run in batch mode without graphics.\n"
00435         << "  -n: Do not execute logon and logoff macros.\n"
00436         << "  -q: Exit after processing command line macro files.\n"
00437         << "  -h: Print this friendly help message.\n"
00438         << "  -H: Print help for modules in list.\n"
00439         << "  -x: Specifies a Job Control Macro to load.\n"
00440         << "      Multiple instances of '-x' are allowed.\n"
00441         << "  -d: Supply database URL (replaces ENV_TSQL_URL)\n"
00442         << "  -u: Supply database user (replaces ENV_TSQL_USER)\n"
00443         << "  -p: Supply database password (replaces ENV_TSQL_PSWD)\n" 
00444         << "  -t: Specify job time limit. Eg. -t'10 minutes'\n"
00445         << "  -r: Specify limit on number of records to process.\n"
00446         << "  -o: Set the name of the default output file.\n"
00447         << "  -v: Set JobC message verbosity [V,D,I,W,E,F].\n";
00448       exit(1);
00449     case 'H': fModuleHelpList = optarg; break;
00450     case 'd': if (optarg) JobCEnvSetenv("ENV_TSQL_URL", optarg); break;
00451     case 'u': if (optarg) JobCEnvSetenv("ENV_TSQL_USER",optarg); break;
00452     case 'p': if (optarg) JobCEnvSetenv("ENV_TSQL_PSWD",optarg); break;
00453     case 'o': fDfltOutFile = optarg; break;
00454     case 'v': // set the MsgService level for JobControl
00455       {
00456         Msg::LogLevel_t lvl = Msg::kInfo;
00457         switch (optarg[0]) {
00458         case 'V': case 'v': lvl = Msg::kVerbose; break;
00459         case 'D': case 'd': lvl = Msg::kDebug;   break;
00460         case 'I': case 'i': lvl = Msg::kInfo;    break;
00461         case 'W': case 'w': lvl = Msg::kWarning; break;
00462         case 'E': case 'e': lvl = Msg::kError;   break;
00463         case 'F': case 'f': lvl = Msg::kFatal;   break;
00464         default:
00465           MSG("JobC",Msg::kWarning)
00466             << "Can not interpret -v level '" << optarg << "'." << endl;
00467         }
00468         MsgStream *s = MsgService::Instance()->GetStream("JobC");
00469         if (s) s->SetLogLevel(lvl);
00470         else
00471           MSG("JobC",Msg::kWarning)
00472             << "Can not find JobC message stream." << endl;
00473         break;
00474       }
00475     default: // Do nothing just pass them along...
00476       MSG("JobC",Msg::kInfo)
00477         << " getopt returned unexpected character code " << c << "." << endl;
00478       break;
00479     }
00480   }
00481 
00482   // Check if the remaining stuff on the command line is:
00483   //   a data file      (.root)
00484   //   a macro script   (.C,.c,.cc,.cxx)
00485   //   a directory name
00486   // If not warn the user in case it's just a simple typo.
00487   for (int i=optind; i<argc; ++i) {
00488     int len = strlen(argv[i]);
00489     if (strcmp(argv[i]+len-5,".root")==0) {
00490       this->AddDataFile(argv[i]);
00491       MSG("JobC",Msg::kDebug)
00492         << "JobCEnv::ProcessCommandLine() added data file: "
00493         << argv[i] << endl;
00494     }
00495     else if ( IsArgMacroFile(argv[i]) != "" ) {
00496       MSG("JobC",Msg::kDebug)
00497         << "JobCEnv::ProcessCommandLine() saw macro script: "
00498         << argv[i] << endl;
00499     }
00500     else {
00501       DIR* d = opendir(argv[i]);
00502       if (d) {
00503         // if ROOT is given a directory it will cd to it before executing
00504         closedir(d);
00505         MSG("JobC",Msg::kDebug)
00506           << "JobCEnv::ProcessCommandLine() saw directory: "
00507           << argv[i] << endl;
00508       }
00509       else {
00510         MSG("JobC",Msg::kInfo)
00511           << "JobCEnv::ProcessCommandLine() unrecognizable arg: "
00512           << argv[i] << endl;
00513       }
00514     }
00515   } // loop over optind
00516 
00517 }
00518 
00519 //......................................................................
00520 
00521 void JobCEnv::AddDataFile(const char *filename)
00522 {
00523 //======================================================================
00524 // Purpose: Add a data file to the list of files on the command line
00525 //======================================================================
00526   assert(filename);
00527   string s(filename);
00528   fDataFileList.push_back(s);
00529 }
00530 
00531 //......................................................................
00532 
00533 void JobCEnv::AddMacroFile(const char *filelist) 
00534 {
00535 //======================================================================
00536 // Inputs: filelist - list of files in format 'file1 file2 fil3'...
00537 //======================================================================
00538   const char *p;
00539   for (p=filelist; *p!='\0';) {
00540     for (; *p==' ' && *p!='\0'; ++p);
00541     if (*p!='\0') {
00542       string s;
00543       s = "";
00544       for (; *p!=' ' && *p!='\0'; ++p) s += *p;
00545       if (s.length()>0) fMacroFileList.push_back(s);
00546     }
00547   }
00548 }
00549 

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