Main Page   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members  

omniEventsLog.cc

Go to the documentation of this file.
00001 // -*- Mode: C++; -*-
00002 //                            Package   : omniEvents
00003 //  omniEventsLog.cc          Created   : 1/10/99
00004 //                            Author    : Paul Nader (pwn)
00005 //
00006 //    Copyright (C) 1998 Paul Nader.
00007 //
00008 //    This file is part of the omniOEvents application.
00009 //
00010 //    omniEvents is free software; you can redistribute it and/or
00011 //    modify it under the terms of the GNU Lesser General Public
00012 //    License as published by the Free Software Foundation; either
00013 //    version 2.1 of the License, or (at your option) any later version.
00014 //
00015 //    omniEvents is distributed in the hope that it will be useful,
00016 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018 //    Lesser General Public License for more details.
00019 //
00020 //    You should have received a copy of the GNU Lesser General Public
00021 //    License along with this library; if not, write to the Free Software
00022 //    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00023 //
00024 // Description:
00025 //      
00026 
00027 /*
00028   $Log: omniEventsLog.cc,v $
00029   Revision 1.4  2003/11/14 14:05:21  alextingle
00030   New output() members functions. Eliminates the need for friend ostream
00031   functions that are problematic on earlier versions of Microsoft
00032   VisualC++. Improved helpfulness of usage and error messages.
00033 
00034   Revision 1.3  2003/11/03 22:41:00  alextingle
00035   Oops! Removed excess log comments.
00036 
00037   Revision 1.2  2003/11/03 22:38:39  alextingle
00038   Removed many platform specific switches, alas many remain. Now uses
00039     autoconf,config.h wherever possible.
00040   Removed local definition of strdup() - omniEvents.cc manages to get
00041     along without it, so it can't be needed here.
00042   Moved convoluted code to obtain hostname into its own header file:
00043     gethostname.h
00044   Moved code that constructs logfile names into its own private method,
00045     omniEventsLog::initializeFileNames()
00046   Added explicit initialisation for all `class omniEventsLog' members.
00047   Moved code that opens file stream into its own private method,
00048     omniEventsLog::openOfstream()
00049 
00050   Revision 1.1.1.1  2002/09/25 19:00:35  shamus13
00051   Import of OmniEvents source tree from release 2.1.1
00052 
00053   Revision 1.8  2000/09/26 09:20:26  naderp
00054   STL Default parameters rework.
00055   Configurable checkpoint period.
00056 
00057   Revision 1.7  2000/09/04 05:05:22  naderp
00058   Fixed problem with triggering multiple checkpoints.
00059 
00060   Revision 1.6  2000/08/30 04:38:24  naderp
00061   Fix to prevent recorder thread from terminating.
00062 
00063   Revision 1.5  2000/08/30 00:57:29  naderp
00064   Fixed broken persist method exiting after first checkpoint.
00065 
00066   Revision 1.4  2000/03/06 13:19:58  naderp
00067   Moved port from global to factory persistency data.
00068 
00069   Revision 1.3  2000/03/06 04:15:09  naderp
00070   Removed internal dependency between factory and Naming Service.
00071 
00072   Revision 1.2  2000/03/02 04:20:09  naderp
00073   Initialising factory refernce in init().
00074 
00075   Revision 1.1  2000/03/02 02:00:25  naderp
00076   Re-open active logfile during re-start.
00077 
00078   Revision 1.0  1999/11/01 17:04:08  naderp
00079   Initial revision
00080 
00081 */
00082 
00083 #include "omniEventsLog.h"
00084 
00085 #ifdef HAVE_CONFIG_H
00086 #  include "config.h"
00087 #endif
00088 
00089 #include <stdio.h>
00090 
00091 #ifdef HAVE_STDLIB_H
00092 #  include <stdlib.h>
00093 #endif
00094 
00095 #ifdef HAVE_SYS_TYPES_H
00096 #  include <sys/types.h>
00097 #endif
00098 
00099 #ifdef HAVE_SYS_STAT_H
00100 #  include <sys/stat.h>
00101 #endif
00102 
00103 #ifdef HAVE_FCNTL_H
00104 #  include <fcntl.h>
00105 #endif
00106 
00107 #if defined(__VMS) && __CRTL_VER < 70000000
00108 #  include <omniVMS/unlink.hxx>
00109 #endif
00110 
00111 #ifdef __WIN32__
00112 #  include <io.h>
00113 #  include <winbase.h>
00114 #  define stat(x,y) _stat(x,y)
00115 #  define unlink(x) _unlink(x)
00116 #endif // __WIN32__
00117 
00118 #ifdef HAVE_UNISTD_H
00119 #  include <unistd.h>
00120 #endif
00121 
00122 #ifdef HAVE_LIBC_H
00123 #  include <libc.h>
00124 #endif
00125 
00126 #ifdef HAVE_SYS_PARAM_H
00127 #  include <sys/param.h>
00128 #endif
00129 
00130 #include <errno.h>
00131 #include <time.h>
00132 #include "gethostname.h"
00133 #include "oep_global.h"
00134 
00135 #include "EventChannelFactory.h"
00136 
00137 //
00138 // Set flags for use in calls to omniEventsLog::openOfstream()
00139 //
00140 
00141 #if defined(HAVE_FSTREAM_OPEN)
00142 #  define FLAG_TRUNCATE ios::trunc
00143 #  define FLAG_APPEND   ios::app
00144 #  define FLAG_SYNC     0
00145 #elif defined(HAVE_FSTREAM_ATTACH)
00146 #  if defined(__WIN32__)
00147 #    define FLAG_SYNC 0
00148 #  elif defined(O_SYNC)
00149 #    define FLAG_SYNC O_SYNC
00150 #  else
00151 #    define FLAG_SYNC O_FSYNC // FreeBSD 3.2 does not have O_SYNC???
00152 #  endif
00153 #  define FLAG_TRUNCATE O_CREAT|O_TRUNC
00154 #  define FLAG_APPEND   O_APPEND
00155 #else
00156 #  error "Can't open a file without ofstream::open() or ofstream::attach()"
00157 #endif
00158 
00159 //
00160 // Minimum idle period before we take a checkpoint (15 mins)
00161 // (Set OMNIEVENTS_LOG_CHECKPOINT_PERIOD in include/config.h.)
00162 //
00163 
00164 #ifndef OMNIEVENTS_LOG_CHECKPOINT_PERIOD
00165 #  define DEFAULT_IDLE_TIME_BTW_CHKPT  (15*60)
00166 #else
00167 #  define DEFAULT_IDLE_TIME_BTW_CHKPT OMNIEVENTS_LOG_CHECKPOINT_PERIOD 
00168 #endif
00169 
00170 //
00171 // Append ';' to VMS filenames to force the latest version.
00172 //
00173 
00174 #ifdef __VMS
00175 #  define VMS_SEMICOLON ";"
00176 #else
00177 #  define VMS_SEMICOLON
00178 #endif
00179 
00180 #define DB(l,x) ((omniORB::traceLevel >= l) && (cerr << x << endl))
00181 
00182 extern int yyparse();
00183 extern int yydebug;
00184 extern FILE *yyin;
00185 
00186 namespace OmniEvents {
00187 
00188 //------------------------------------------------------------------------
00189 //           timestamp Implementation
00190 //------------------------------------------------------------------------
00191 // This class can be used to generate timestamps.  The t() method normally
00192 // returns a timestamp string, but if the same timestamp (to the nearest
00193 // second) would be returned as last time then an empty string is returned
00194 // instead.
00195 //
00196 
00197 class timestamp {
00198   char str[29];
00199 public:
00200   timestamp(void) {
00201     str[0] = '\n';
00202     str[1] = str[28] = '\0';
00203   }
00204   char* t(void) {
00205     time_t t = time(NULL);
00206     char *p = ctime(&t);
00207     if (strncmp(p, &str[1], 24) == 0) {
00208       return &str[28];
00209     }
00210     strncpy(&str[1], p, 24);
00211     str[25] = ':';
00212     str[26] = '\n';
00213     str[27] = '\n';
00214     return str;
00215   }
00216 };
00217 
00218 timestamp ts;
00219 
00220 //------------------------------------------------------------------------
00221 //           omniEvents Log Implementation
00222 //------------------------------------------------------------------------
00223 
00224 omniEventsLog *omniEventsLog::theLog = NULL;
00225 
00226 omniEventsLog::omniEventsLog(int p, const char* logdir) :
00227   port(p),
00228   logfile(),
00229   firstTime(0),
00230   active(NULL),
00231   backup(NULL),
00232   checkpt(NULL),
00233   recorder(NULL),
00234   factory(NULL),
00235   checkpointNeeded(1),
00236   _lock()
00237 {
00238   omniEventsLog::theLog = this;
00239   
00240   initializeFileNames(logdir);
00241 
00242 #ifdef __WIN32__
00243   struct _stat sb;
00244 #else
00245   struct stat sb;
00246 #endif
00247 
00248   if (port != 0)
00249   {
00250 
00251     //
00252     // Starting for the first time - make sure log file doesn't exist, and
00253     // for safety, that there is no backup file either.
00254 
00255     firstTime = 1;
00256 
00257     if (stat(active,&sb) == 0)
00258     {
00259       cerr << ts.t() << 
00260         "Error: persistency log file '" << active << "' exists.\n"
00261         " Run without the -s option to recover the"
00262         " server's state, or remove the file\n"
00263         " (and any backup) and then run with -s to"
00264         " create a new persistency log." << endl;
00265       exit(1);
00266     }
00267     if (stat(backup,&sb) == 0)
00268     {
00269       cerr << ts.t() <<
00270         "Error: backup file '" << backup << "' exists.\n"
00271         " Rename it to '" << active << "'\n"
00272         " and then run without the -s option to"
00273         " recover the server's state, or delete\n"
00274         " it and then run with -s to create a new persistency log." << endl;
00275       exit(1);
00276     }
00277   }
00278   else
00279   {
00280 
00281     //
00282     // Restart - parse log file.
00283 
00284     firstTime = 0;
00285 
00286     FILE *file = fopen(active, "r");
00287 
00288     if (!file)
00289     {
00290       cerr << ts.t() << "Error: cannot open persistency log file '" << active
00291            << "'." << endl;
00292 
00293       if (stat(backup,&sb) == 0) 
00294       {
00295         cerr <<
00296          " Backup file '" << backup << "' exists.\n"
00297          " Either rename it to '" << active << "' to\n"
00298          " recover the server's state, or delete it and"
00299          " then run with the -s option to\n"
00300          " create a new persistency log." <<endl;
00301       }
00302       else
00303       {
00304         cerr <<
00305          " Use the -s option the first time you run the server, to create the\n"
00306          " persistency log file." << endl;
00307       }
00308       exit(1);
00309     }
00310 
00311     yyin = file;
00312     oep_global = new OEP_GlobalData();
00313     int result = yyparse();
00314     if (result == 1)
00315     {
00316       cerr << ts.t() << "Error: parsing log file '" << active << "'." << endl;
00317       exit(1);
00318     }
00319     fclose(file);
00320     cerr << ts.t() << "Read log file successfully." << endl;
00321 
00322     //
00323     // Set the server port number
00324     OEP_cfps *fdata = oep_global->getFactory();
00325     port = fdata->getPort();
00326   }
00327 }
00328 
00329 void
00330 omniEventsLog::init(EventChannelFactory_i *&f)
00331 {
00332   factory = f;
00333 
00334   try
00335   {
00336     if (firstTime)
00337     {
00338       //
00339       // starting for the first time - create an initial log file.
00340       cerr << ts.t() << "Starting omniEvents on port "
00341            << port << endl;
00342 
00343       openOfstream(logfile,active,FLAG_TRUNCATE|FLAG_SYNC);
00344       _lock.lock();
00345       output(logfile);
00346       _lock.unlock();
00347 
00348       checkpointNeeded = 1;
00349       cerr << ts.t() << "Wrote initial log file.\n" << endl;
00350     }
00351     else
00352     {
00353       openOfstream(logfile,active,FLAG_APPEND);
00354 
00355       //
00356       // Get the persisted factory data
00357       OEP_cfps fdata =*oep_global->getFactory();
00358 
00359       //
00360       // Create the default factory
00361       factory =new EventChannelFactory_i(fdata.getPort(),fdata.getChannels());
00362       f = factory;
00363     }
00364   }
00365   catch (IOError& ex)
00366   {
00367     cerr << ts.t() << "Error: cannot open" << (firstTime?" initial":"")
00368          << " log file '" << active
00369          << "': " << endl;
00370     perror("");
00371     cerr << "\nYou can set the environment variable "
00372          << OMNIEVENTS_LOGDIR_ENV_VAR
00373          << " to specify the\ndirectory where the log files are kept.\n"
00374          << endl;
00375     logfile.close();
00376     unlink(active);
00377     exit(1);
00378   }
00379 
00380   //
00381   // Create a worker thread to checkpoint the log file at regular intervals.
00382   recorder = new omniEventsLogWorker(this,
00383                                      &omniEventsLog::checkpoint,
00384                                      omni_thread::PRIORITY_NORMAL);
00385   return;
00386 }
00387 
00388 void
00389 omniEventsLog::persist()
00390 {
00391   omniEventsLog *log = omniEventsLog::theLog;
00392   if(log && log->factory)
00393   {
00394     omni_mutex_lock l(log->_lock);
00395     log->output(log->logfile);
00396     log->logfile.flush();
00397     log->checkpointNeeded = 1;
00398   }
00399 }
00400 
00401 void
00402 omniEventsLog::output(ostream& os)
00403 {
00404   factory->output(os);
00405   os<<endl;
00406 }
00407 
00408 int
00409 omniEventsLog::getPort()
00410 {
00411   return port;
00412 }
00413 
00414 void
00415 omniEventsLog::checkpoint(void)
00416 {
00417 
00418   int idle_time_btw_chkpt;
00419   static int firstCheckPoint = 1;
00420   char *itbc = getenv("OMNIEVENTS_ITBC");
00421   if (itbc == NULL || sscanf(itbc,"%d",&idle_time_btw_chkpt) != 1)
00422   {
00423     idle_time_btw_chkpt = DEFAULT_IDLE_TIME_BTW_CHKPT;
00424   }
00425 
00426   omni_mutex mutex;
00427   omni_condition cond(&mutex);
00428 
00429   mutex.lock();
00430   while (1) {
00431 
00432     // Take an initial checkpoint the first time. All subsequent
00433     // checkpoints are conditionally tested on whether they are
00434     // needed or not.
00435 
00436     if (! firstCheckPoint)
00437     {
00438        unsigned long s, n;
00439        omni_thread::get_time(&s, &n, idle_time_btw_chkpt);
00440        cond.timedwait(s,n);
00441 
00442        _lock.lock();
00443        if (!checkpointNeeded)
00444        {
00445           _lock.unlock();
00446           continue;
00447        }
00448     }
00449     else
00450     {
00451        _lock.lock();
00452        firstCheckPoint = 0;
00453     }
00454   
00455     cerr << ts.t() << "Checkpointing Phase 1: Prepare." << endl;
00456   
00457     ofstream ckpf;
00458     int fd = -1;
00459   
00460     try
00461     {
00462       try
00463       {
00464         openOfstream(ckpf,checkpt,FLAG_TRUNCATE|FLAG_SYNC,&fd);
00465       }
00466       catch (IOError& ex)
00467       {
00468         cerr << ts.t() << "Error: cannot open checkpoint file '"
00469              << checkpt << "' for writing." << endl;
00470         throw;
00471       }
00472   
00473       output(ckpf);
00474   
00475       ckpf.close();
00476       if (!ckpf)
00477         throw IOError();
00478   
00479   // a bug in sparcworks C++ means that the fd doesn't get closed.
00480 #if defined(__sunos__) && defined(__SUNPRO_CC) && __SUNPRO_CC < 0x500
00481       if (close(fd) < 0)
00482         throw IOError();
00483 #endif
00484   
00485     } catch (IOError& ex) {
00486       cerr << ts.t() << flush;
00487       perror("I/O error writing checkpoint file");
00488       cerr << "Abandoning checkpoint" << endl;
00489       ckpf.close();
00490   // a bug in sparcworks C++ means that the fd doesn't get closed.
00491 #if defined(__sunos__) && defined(__SUNPRO_CC) && __SUNPRO_CC < 0x500
00492       close(fd);
00493 #endif
00494       unlink(checkpt);
00495       _lock.unlock();
00496       continue;
00497     }
00498   
00499     //
00500     // Now commit the checkpoint to become the active log.
00501     //
00502   
00503     cerr << ts.t() << "Checkpointing Phase 2: Commit." << endl;
00504 
00505   // a bug in sparcworks C++ means that the fd doesn't get closed.
00506 #if defined(__sunos__) && defined(__SUNPRO_CC) && __SUNPRO_CC < 0x500
00507     close(logfile.rdbuf()->fd());
00508 #endif
00509   
00510     logfile.close();
00511   
00512     unlink(backup);
00513   
00514 #if defined(__WIN32__)
00515     if (rename(active, backup) != 0) {
00516 #elif defined(__VMS)
00517     if (rename(active, backup) < 0) {
00518 #else
00519     if (link(active,backup) < 0) {
00520 #endif
00521       // Failure here leaves old active and checkpoint file.
00522       cerr << ts.t() << "Error: failed to link backup file '" << backup
00523            << "' to old log file '" << active << "'." << endl;
00524       exit(1);
00525     }
00526   
00527 #if !defined( __VMS) && !defined(__WIN32__)
00528     if (unlink(active) < 0) {
00529       // Failure here leaves active and backup pointing to the same (old) file.
00530       cerr << ts.t() << "Error: failed to unlink old log file '" << active
00531            << "'." << endl;
00532       perror("");
00533       exit(1);
00534     }
00535 #endif
00536   
00537 #if defined(__WIN32__)
00538     if (rename(checkpt,active) != 0) {
00539 #elif defined(__VMS)
00540     if (rename(checkpt,active) < 0) {
00541 #else
00542     if (link(checkpt,active) < 0) {
00543 #endif
00544       // Failure here leaves no active but backup points to the old file.
00545       cerr << ts.t() << "Error: failed to link log file '" << active
00546            << "' to checkpoint file '" << checkpt << "'." << endl;
00547       exit(1);
00548     }
00549   
00550 #if !defined( __VMS) && !defined(__WIN32__)
00551     if (unlink(checkpt) < 0) {
00552       // Failure here leaves active and checkpoint pointing to the same file.
00553       cerr << ts.t() << "Error: failed to unlink checkpoint file '" << checkpt
00554            << "'." << endl;
00555       exit(1);
00556     }
00557 #endif
00558   
00559     try
00560     {
00561       openOfstream(logfile,active,FLAG_APPEND|FLAG_SYNC,&fd);
00562     }
00563     catch (IOError& ex)
00564     {
00565       cerr << ts.t() << "Error: cannot open new log file '" << active
00566            << "' for writing." << endl;
00567       exit(1);
00568     }
00569   
00570     cerr << ts.t() << "Checkpointing completed." << endl;
00571   
00572     checkpointNeeded = 0;
00573     _lock.unlock();
00574   }
00575   mutex.unlock();
00576 }
00577 
00578 
00589 void omniEventsLog::initializeFileNames(const char* logdir)
00590 {
00591   if (!logdir)
00592       logdir = getenv(OMNIEVENTS_LOGDIR_ENV_VAR);
00593   if (!logdir)
00594   {
00595 #if defined(OMNIEVENTS_LOG_DEFAULT_LOCATION)
00596     logdir = OMNIEVENTS_LOG_DEFAULT_LOCATION;
00597 #elif defined(__WIN32__)
00598     logdir = "C:\\TEMP";
00599 #elif defined(__VMS)
00600     logdir = "[]"; // What is a good default for VMS?
00601 #else // Unix
00602     logdir = "/var/omniEvents";
00603 #endif
00604   }
00605 
00606   const char* logname ="omnievents-";
00607   char hostname[MAXHOSTNAMELEN];
00608   if (0!=gethostname(hostname,MAXHOSTNAMELEN))
00609   {
00610     cerr << ts.t() << "Error: cannot get the name of this host." << endl;
00611     exit(1);
00612   }
00613   const char* sep ="";
00614 
00615 #if defined(__WIN32__)
00616   sep="\\";
00617 #elif defined(__VMS)
00618   char last( logdir[strlen(logdir)-1] );
00619   if (last != ':' && last != ']')
00620   {
00621     cerr << ts.t() << "Error: " << OMNIEVENTS_LOGDIR_ENV_VAR << " (" << logdir
00622          << ") is not a directory name." << endl;
00623     exit(1);
00624   }
00625 #else // Unix
00626   if (logdir[0] != '/')
00627   {
00628     cerr << ts.t() << "Error: " << OMNIEVENTS_LOGDIR_ENV_VAR << " (" << logdir
00629          << ") is not an absolute path name." << endl;
00630     exit(1);
00631   }
00632   if (logdir[strlen(logdir)-1] != '/')
00633       sep="/";
00634 #endif
00635 
00636   // VMS_SEMICOLON specifies latest version of the file on VMS
00637   // (essentially, we're saying we don't want to use VMS file versioning).
00638 
00639   setFilename(active, logdir,sep,logname,hostname,".log" VMS_SEMICOLON);
00640   setFilename(backup, logdir,sep,logname,hostname,".bak" VMS_SEMICOLON);
00641   setFilename(checkpt,logdir,sep,logname,hostname,".ckp" VMS_SEMICOLON);
00642 }
00643 
00644 
00648 void
00649 omniEventsLog::setFilename(
00650   char*& filename,     const char* logdir,   const char* sep,
00651   const char* logname, const char* hostname, const char* ext)
00652 {
00653   size_t len=1+
00654     strlen(logdir)+strlen(sep)+strlen(logname)+strlen(hostname)+strlen(ext);
00655   filename=new char[len];
00656   sprintf(filename,"%s%s%s%s%s",logdir,sep,logname,hostname,ext);
00657 }
00658 
00659 
00673 void
00674 omniEventsLog::openOfstream(
00675   ofstream& s, const char* filename, int flags, int* fd)
00676 {
00677 #if defined(HAVE_FSTREAM_OPEN)
00678 #  ifdef FSTREAM_OPEN_PROT
00679       s.open(filename,ios::out|flags,0666);
00680 #  else
00681       s.open(filename,ios::out|flags);
00682 #  endif
00683       if (!s)
00684           throw IOError();
00685 
00686 #elif defined(HAVE_FSTREAM_ATTACH)
00687 #  ifdef __WIN32__
00688       int localFd = _open(filename, O_WRONLY | flags, _S_IWRITE);
00689 #  else
00690       int localFd = open(filename, O_WRONLY | flags, 0666);
00691 #  endif /* __WIN32__ */
00692       if (localFd < 0)
00693           throw IOError();
00694       if(fd)
00695           (*fd)=localFd;
00696       s.attach(localFd);
00697 #endif
00698 }
00699 
00700 
00701 //------------------------------------------------------------------------
00702 //           OmniEvents Log Worker Implementation
00703 //------------------------------------------------------------------------
00704 omniEventsLogWorker::omniEventsLogWorker(omniEventsLog *object,
00705                                        Method method,
00706                                        priority_t priority) :
00707    omni_thread(NULL, priority)
00708 {
00709 
00710    DB(10, "omniEventsLogWorker : Constructor Start");
00711 
00712    _method = method;
00713    _object = object;
00714 
00715    start_undetached();
00716 
00717    DB(10, "omniEventsLogWorker : Constructor End");
00718 }
00719 
00720 void*
00721 omniEventsLogWorker::run_undetached (void *) {
00722 
00723    DB(10, "omniEventsLogWorker : run_undetached Start");
00724 
00725    (_object->*_method)();
00726 
00727    DB(10, "omniEventsLogWorker : run_undetached End");
00728 
00729    return(0);
00730 }
00731 
00732 omniEventsLogWorker::~omniEventsLogWorker () {
00733 
00734    DB(10, "omniEventsLogWorker : Destructor Start");
00735 
00736    DB(10, "omniEventsLogWorker : Destructor End");
00737 }
00738 
00739 
00740 }; // end namespace OmniEvents
00741 

Generated on Fri Dec 12 10:53:02 2003 for OmniEvents by doxygen1.2.15