src/corelib/ncbiapp.cpp

Go to the documentation of this file.
00001 /*  $Id: ncbiapp.cpp 175750 2009-11-10 15:23:04Z ivanov $
00002  * ===========================================================================
00003  *
00004  *                            PUBLIC DOMAIN NOTICE
00005  *               National Center for Biotechnology Information
00006  *
00007  *  This software/database is a "United States Government Work" under the
00008  *  terms of the United States Copyright Act.  It was written as part of
00009  *  the author's official duties as a United States Government employee and
00010  *  thus cannot be copyrighted.  This software/database is freely available
00011  *  to the public for use. The National Library of Medicine and the U.S.
00012  *  Government have not placed any restriction on its use or reproduction.
00013  *
00014  *  Although all reasonable efforts have been taken to ensure the accuracy
00015  *  and reliability of the software and data, the NLM and the U.S.
00016  *  Government do not and cannot warrant the performance or results that
00017  *  may be obtained by using this software or data. The NLM and the U.S.
00018  *  Government disclaim all warranties, express or implied, including
00019  *  warranties of performance, merchantability or fitness for any particular
00020  *  purpose.
00021  *
00022  *  Please cite the author in any work or product based on this material.
00023  *
00024  * ===========================================================================
00025  *
00026  * Authors:  Vsevolod Sandomirskiy, Denis Vakatov
00027  *
00028  * File Description:
00029  *   CNcbiApplication -- a generic NCBI application class
00030  *   CCgiApplication  -- a NCBI CGI-application class
00031  *
00032  */
00033 
00034 #include <ncbi_pch.hpp>
00035 #include <corelib/ncbiapp.hpp>
00036 #include <corelib/ncbifile.hpp>
00037 #include <corelib/ncbi_system.hpp>
00038 #include <corelib/ncbi_param.hpp>
00039 #include <corelib/syslog.hpp>
00040 #include <corelib/error_codes.hpp>
00041 #include <corelib/ncbi_safe_static.hpp>
00042 
00043 #if defined(NCBI_OS_MSWIN)
00044 #  include <corelib/ncbi_os_mswin.hpp>
00045 #  include <corelib/ncbidll.hpp>
00046 #  include <io.h>
00047 #  include <fcntl.h>
00048 #endif
00049 
00050 #if defined(NCBI_OS_UNIX)
00051 #  include <unistd.h>
00052 #endif
00053 
00054 
00055 #define NCBI_USE_ERRCODE_X   Corelib_App
00056 
00057 
00058 BEGIN_NCBI_SCOPE
00059 
00060 
00061 /////////////////////////////////////////////////////////////////////////////
00062 //  Constants
00063 //
00064 
00065 static const char* s_ArgLogFile     = "-logfile";
00066 static const char* s_ArgCfgFile     = "-conffile";
00067 static const char* s_ArgVersion     = "-version";
00068 static const char* s_ArgFullVersion = "-version-full";
00069 static const char* s_ArgDryRun      = "-dryrun";
00070 
00071 
00072 /////////////////////////////////////////////////////////////////////////////
00073 //  Global variables
00074 //
00075 
00076 static bool s_IsApplicationStarted = false;
00077 
00078 
00079 ///////////////////////////////////////////////////////
00080 // CNcbiApplication
00081 //
00082 
00083 CNcbiApplication* CNcbiApplication::m_Instance;
00084 
00085 
00086 CNcbiApplication* CNcbiApplication::Instance(void)
00087 {
00088     return m_Instance;
00089 }
00090 
00091 
00092 CNcbiApplication::CNcbiApplication(void)
00093 {
00094     // Initialize UID and start timer
00095     GetDiagContext().GetUID();
00096     GetDiagContext().InitMessages(size_t(-1));
00097     GetDiagContext().SetGlobalAppState(eDiagAppState_AppBegin);
00098 
00099     m_DisableArgDesc = 0;
00100     m_HideArgs = 0;
00101     m_StdioFlags = 0;
00102     m_CinBuffer = 0;
00103     m_ExitCodeCond = eNoExits;
00104 
00105     // Register the app. instance
00106     if ( m_Instance ) {
00107         NCBI_THROW(CAppException, eSecond,
00108                    "Second instance of CNcbiApplication is prohibited");
00109     }
00110     m_Instance = this;
00111 
00112     // Create empty version info
00113     m_Version.Reset(new CVersion());
00114 
00115     // Create empty application arguments & name
00116     m_Arguments.reset(new CNcbiArguments(0,0));
00117 
00118     // Create empty application environment
00119     m_Environ.reset(new CNcbiEnvironment);
00120 
00121     // Create an empty registry
00122     m_Config.Reset(new CNcbiRegistry);
00123 
00124     m_DryRun = false;
00125 }
00126 
00127 
00128 CNcbiApplication::~CNcbiApplication(void)
00129 {
00130     m_Instance = 0;
00131     FlushDiag(0, true);
00132     if (m_CinBuffer) {
00133         delete [] m_CinBuffer;
00134     }
00135 
00136 #if defined(NCBI_COMPILER_WORKSHOP)
00137     // At least under these conditions:
00138     //  1) WorkShop 5.5 on Solaris 10/SPARC, Release64MT,    and
00139     //  2) when IOS_BASE::sync_with_stdio(false) is called,  and
00140     //  3) the contents of 'cout' is not flushed
00141     // some applications crash on exit() while apparently trying to
00142     // flush 'cout' and getting confused by its own guts, with error:
00143     //   "*** libc thread failure: _thread_setschedparam_main() fails"
00144     //
00145     // This forced pre-flush trick seems to fix the problem.
00146     NcbiCout.flush();
00147 #endif
00148 }
00149 
00150 
00151 void CNcbiApplication::Init(void)
00152 {
00153     return;
00154 }
00155 
00156 
00157 int CNcbiApplication::DryRun(void)
00158 {
00159     ERR_POST_X(1, Info << "DryRun: default implementation does nothing");
00160     return 0;
00161 }
00162 
00163 
00164 void CNcbiApplication::Exit(void)
00165 {
00166     return;
00167 }
00168 
00169 
00170 const CArgs& CNcbiApplication::GetArgs(void) const
00171 {
00172     if ( !m_Args.get() ) {
00173         NCBI_THROW(CAppException, eUnsetArgs,
00174                    "Command-line argument description is not found");
00175     }
00176     return *m_Args;
00177 }
00178 
00179 
00180 SIZE_TYPE CNcbiApplication::FlushDiag(CNcbiOstream* os, bool /*close_diag*/)
00181 {
00182     if ( os ) {
00183         SetDiagStream(os, true, 0, 0, "STREAM");
00184     }
00185     GetDiagContext().FlushMessages(*GetDiagHandler());
00186     GetDiagContext().DiscardMessages();
00187     return 0;
00188 }
00189 
00190 
00191 #if defined(NCBI_OS_DARWIN)
00192 static void s_MacArgMunging(CNcbiApplication&   app,
00193                             int*                argcPtr,
00194                             const char* const** argvPtr,
00195                             const string&       exepath)
00196 {
00197 
00198     // Sometimes on Mac there will be an argument -psn which
00199     // will be followed by the Process Serial Number, e.g. -psn_0_13107201
00200     // this is in situations where the application could have no other
00201     // arguments like when it is double clicked.
00202     // This will mess up argument processing later, so get rid of it.
00203     static const char* s_ArgMacPsn = "-psn_";
00204 
00205     if (*argcPtr == 2  &&
00206         NStr::strncmp((*argvPtr)[1], s_ArgMacPsn, strlen(s_ArgMacPsn)) == 0) {
00207         --*argcPtr;
00208     }
00209 
00210     if (*argcPtr > 1)
00211         return;
00212 
00213     // Have no arguments from the operating system -- so use the '.args' file
00214 
00215     // Open the args file.
00216     string exedir;
00217     CDir::SplitPath(exepath, &exedir);
00218     string args_fname = exedir + app.GetProgramDisplayName() + ".args";
00219     CNcbiIfstream in(args_fname.c_str());
00220 
00221     if ( !in.good() ) {
00222         ERR_POST_X(2, Info << "Mac arguments file not found: " << args_fname);
00223         return;
00224     }
00225 
00226     vector<string> v;
00227 
00228     // remember or fake the executable name.
00229     if (*argcPtr > 0) {
00230         v.push_back((*argvPtr)[0]); // preserve the original argv[0].
00231     } else {
00232         v.push_back(exepath);
00233     }
00234 
00235     // grab the rest of the arguments from the file.
00236     // arguments are separated by whitespace. Can be on
00237     // more than one line.
00238     string arg;
00239     while (in >> arg) {
00240         v.push_back(arg);
00241     }
00242 
00243     // stash them away in the standard argc and argv places.
00244     *argcPtr = v.size();
00245 
00246     char** argv =  new char*[v.size()];
00247     int c = 0;
00248     ITERATE(vector<string>, vp, v) {
00249         argv[c++] = strdup(vp->c_str());
00250     }
00251     *argvPtr = argv;
00252 }
00253 #endif  /* NCBI_OS_DARWIN */
00254 
00255 
00256 NCBI_PARAM_DECL(bool, Debug, Catch_Unhandled_Exceptions);
00257 NCBI_PARAM_DEF_EX(bool, Debug, Catch_Unhandled_Exceptions, true,
00258                   eParam_NoThread,
00259                   DEBUG_CATCH_UNHANDLED_EXCEPTIONS);
00260 typedef NCBI_PARAM_TYPE(Debug, Catch_Unhandled_Exceptions) TParamCatchExceptions;
00261 
00262 bool s_HandleExceptions(void)
00263 {
00264     return TParamCatchExceptions::GetDefault();
00265 }
00266 
00267 
00268 void CNcbiApplication::x_TryInit(EAppDiagStream diag,
00269                                  const char*    conf)
00270 {
00271     // Load registry from the config file
00272     if ( conf ) {
00273         string x_conf(conf);
00274         LoadConfig(*m_Config, &x_conf);
00275     } else {
00276         LoadConfig(*m_Config, NULL);
00277     }
00278 
00279     CDiagContext::SetupDiag(diag, m_Config, eDCM_Flush);
00280     CDiagContext::x_FinalizeSetupDiag();
00281 
00282     // Setup the standard features from the config file.
00283     // Don't call till after LoadConfig()
00284     // NOTE: this will override environment variables,
00285     // except DIAG_POST_LEVEL which is Set*Fixed*.
00286     x_HonorStandardSettings();
00287 
00288     // Application start
00289     AppStart();
00290 
00291     // Do init
00292 #if (defined(NCBI_COMPILER_ICC) && NCBI_COMPILER_VERSION < 900)
00293     // ICC 8.0 have an optimization bug in exceptions handling,
00294     // so workaround it here
00295     try {
00296         Init();
00297     }
00298     catch (CArgHelpException& ) {
00299         throw;
00300     }
00301 #else
00302     Init();
00303 #endif
00304 
00305     // If the app still has no arg description - provide default one
00306     if (!m_DisableArgDesc  &&  !m_ArgDesc.get()) {
00307         auto_ptr<CArgDescriptions> arg_desc(new CArgDescriptions);
00308         arg_desc->SetUsageContext
00309             (GetArguments().GetProgramBasename(),
00310              "This program has no mandatory arguments");
00311         SetupArgDescriptions(arg_desc.release());
00312     }
00313 }
00314 
00315 
00316 void CNcbiApplication::x_TryMain(EAppDiagStream diag,
00317                                  const char*    conf,
00318                                  int*           exit_code,
00319                                  bool*          got_exception)
00320 {
00321     // Initialize the application
00322     if ( s_HandleExceptions() ) {
00323         try {
00324             x_TryInit(diag, conf);
00325         }
00326         catch (CArgHelpException& e) {
00327             x_AddDefaultArgs();
00328             // Print USAGE
00329             if (e.GetErrCode() == CArgHelpException::eHelpXml) {
00330                 m_ArgDesc->PrintUsageXml(cout);
00331             } else {
00332                 string str;
00333                 m_ArgDesc->PrintUsage
00334                     (str, e.GetErrCode() == CArgHelpException::eHelpFull);
00335                 cout << str;
00336             }
00337             *exit_code = 0;
00338         }
00339         catch (CArgException& e) {
00340             NCBI_RETHROW_SAME(e, "Application's initialization failed");
00341         }
00342         catch (CException& e) {
00343             NCBI_REPORT_EXCEPTION_X(15,
00344                                     "Application's initialization failed", e);
00345             *got_exception = true;
00346             *exit_code = 2;
00347         }
00348         catch (exception& e) {
00349             ERR_POST_X(6, "Application's initialization failed: " << e.what());
00350             *got_exception = true;
00351             *exit_code = 2;
00352         }
00353     }
00354     else {
00355         x_TryInit(diag, conf);
00356     }
00357 
00358     // Run application
00359     if (*exit_code == 1) {
00360         GetDiagContext().SetGlobalAppState(eDiagAppState_AppRun);
00361         if ( s_HandleExceptions() ) {
00362             try {
00363                 *exit_code = m_DryRun ? DryRun() : Run();
00364             }
00365             catch (CArgException& e) {
00366                 NCBI_RETHROW_SAME(e, "Application's execution failed");
00367             }
00368             catch (CException& e) {
00369                 NCBI_REPORT_EXCEPTION_X(16,
00370                                         "Application's execution failed", e);
00371                 *got_exception = true;
00372                 *exit_code = 3;
00373             }
00374             catch (exception& e) {
00375                 ERR_POST_X(7, "Application's execution failed: " << e.what());
00376                 *got_exception = true;
00377                 *exit_code = 3;
00378             }
00379         }
00380         else {
00381             *exit_code = m_DryRun ? DryRun() : Run();
00382         }
00383     }
00384     GetDiagContext().SetGlobalAppState(eDiagAppState_AppEnd);
00385 
00386     // Close application
00387     if ( s_HandleExceptions() ) {
00388         try {
00389             Exit();
00390         }
00391         catch (CArgException& e) {
00392             NCBI_RETHROW_SAME(e, "Application's cleanup failed");
00393         }
00394         catch (CException& e) {
00395             NCBI_REPORT_EXCEPTION_X(17, "Application's cleanup failed", e);
00396             *got_exception = true;
00397         }
00398         catch (exception& e) {
00399             ERR_POST_X(8, "Application's cleanup failed: "<< e.what());
00400             *got_exception = true;
00401         }
00402     }
00403     else {
00404         Exit();
00405     }
00406 }
00407 
00408 
00409 int CNcbiApplication::AppMain
00410 (int                argc,
00411  const char* const* argv,
00412  const char* const* envp,
00413  EAppDiagStream     diag,
00414  const char*        conf,
00415  const string&      name)
00416 {
00417     if (conf) {
00418         m_DefaultConfig = conf;
00419     }
00420     x_SetupStdio();
00421 
00422     // Get program executable's name & path.
00423     string exepath = FindProgramExecutablePath(argc, argv, &m_RealExePath);
00424     m_ExePath = exepath;
00425 
00426     // Get program display name
00427     string appname = name;
00428     if (appname.empty()) {
00429         if (!exepath.empty()) {
00430             CDirEntry::SplitPath(exepath, NULL, &appname);
00431         } else if (argc > 0  &&  argv[0] != NULL  &&  *argv[0] != '\0') {
00432             CDirEntry::SplitPath(argv[0], NULL, &appname);
00433         } else {
00434             appname = "ncbi";
00435         }
00436     }
00437     if ( m_ProgramDisplayName.empty() ) {
00438         SetProgramDisplayName(appname);
00439     }
00440 
00441     // Make sure we have something as our 'real' executable's name.
00442     // though if it does not contain a full path it won't be much use.
00443     if ( exepath.empty() ) {
00444         ERR_POST_X(3, Warning
00445                       << "Warning:  Could not determine this application's "
00446                       "file name and location.  Using \""
00447                       << appname << "\" instead.\n"
00448                       "Please fix FindProgramExecutablePath() on this platform.");
00449         exepath = appname;
00450     }
00451 
00452 #if defined(NCBI_OS_DARWIN)
00453     // We do not know standard way of passing arguments to C++ program on Mac,
00454     // so we will read arguments from special file having extension ".args"
00455     // and name equal to display name of program (name argument of AppMain).
00456     s_MacArgMunging(*this, &argc, &argv, exepath);
00457 #endif
00458 
00459     // Preparse command line
00460     if (PreparseArgs(argc, argv) == ePreparse_Exit) {
00461         GetDiagContext().DiscardMessages();
00462         return 0;
00463     }
00464 
00465     // Check command line for presence special arguments
00466     // "-logfile", "-conffile", "-version"
00467     bool is_diag_setup = false;
00468     if (!m_DisableArgDesc && argc > 1  &&  argv) {
00469         const char** v = new const char*[argc];
00470         v[0] = argv[0];
00471         int real_arg_index = 1;
00472         for (int i = 1;  i < argc;  i++) {
00473             if ( !argv[i] ) {
00474                 continue;
00475             }
00476             // Log file - ignore if diag is eDS_User - the user wants to
00477             // take care about logging.
00478             if ( diag != eDS_User  &&
00479                 NStr::strcmp(argv[i], s_ArgLogFile) == 0 ) {
00480                 if ( !argv[i++] ) {
00481                     continue;
00482                 }
00483                 v[real_arg_index++] = argv[i - 1];
00484                 v[real_arg_index++] = argv[i];
00485                 if (SetLogFile(argv[i], eDiagFile_All, true)) {
00486                     diag = eDS_User;
00487                     is_diag_setup = true;
00488                 }
00489                 // Configuration file
00490             } else if ( NStr::strcmp(argv[i], s_ArgCfgFile) == 0 ) {
00491                 if ( !argv[i++] ) {
00492                     continue;
00493                 }
00494                 v[real_arg_index++] = argv[i - 1];
00495                 v[real_arg_index++] = argv[i];
00496                 conf = argv[i];
00497 
00498                 // Version
00499             } else if ( NStr::strcmp(argv[i], s_ArgVersion) == 0 ) {
00500                 if ( !argv[i++] ) {
00501                     continue;
00502                 }
00503                 // Print VERSION
00504                 cout << GetFullVersion().Print( appname,
00505                     CVersion::fVersionInfo | CVersion::fPackageShort );
00506                 GetDiagContext().DiscardMessages();
00507                 return 0;
00508 
00509                 // Full version
00510             } else if ( NStr::strcmp(argv[i], s_ArgFullVersion) == 0 ) {
00511                 if ( !argv[i++] ) {
00512                     continue;
00513                 }
00514                 // Print full VERSION
00515                 cout << GetFullVersion().Print( appname );
00516                 GetDiagContext().DiscardMessages();
00517                 return 0;
00518 
00519                 // Dry run
00520             } else if ( NStr::strcmp(argv[i], s_ArgDryRun) == 0 ) {
00521                 m_DryRun = true;
00522 
00523                 // Save real argument
00524             } else {
00525                 v[real_arg_index++] = argv[i];
00526             }
00527         }
00528         if (real_arg_index == argc ) {
00529             delete[] v;
00530         } else {
00531             argc = real_arg_index;
00532             argv = v;
00533         }
00534     }
00535 
00536     // Reset command-line args and application name
00537     m_Arguments->Reset(argc, argv, exepath, m_RealExePath);
00538 
00539     // Reset application environment
00540     m_Environ->Reset(envp);
00541 
00542     // Setup some debugging features from environment variables.
00543     if ( !m_Environ->Get(DIAG_TRACE).empty() ) {
00544         SetDiagTrace(eDT_Enable, eDT_Enable);
00545     }
00546     string post_level = m_Environ->Get(DIAG_POST_LEVEL);
00547     if ( !post_level.empty() ) {
00548         EDiagSev sev;
00549         if (CNcbiDiag::StrToSeverityLevel(post_level.c_str(), sev)) {
00550             SetDiagFixedPostLevel(sev);
00551         }
00552     }
00553     if ( !m_Environ->Get(ABORT_ON_THROW).empty() ) {
00554         SetThrowTraceAbort(true);
00555     }
00556 
00557     // Clear registry content
00558     m_Config->Clear();
00559 
00560     // Setup for diagnostics
00561     try {
00562         if ( !is_diag_setup ) {
00563             CDiagContext::SetupDiag(diag);
00564         }
00565     } catch (CException& e) {
00566         NCBI_RETHROW(e, CAppException, eSetupDiag,
00567                      "Application diagnostic stream's setup failed");
00568     } catch (exception& e) {
00569         NCBI_THROW(CAppException, eSetupDiag,
00570                    "Application diagnostic stream's setup failed: " +
00571                    string(e.what()));
00572     }
00573 
00574     // Call:  Init() + Run() + Exit()
00575     int exit_code = 1;
00576     bool got_exception = false;
00577     s_IsApplicationStarted = true;
00578 
00579     if ( s_HandleExceptions() ) {
00580         try {
00581             x_TryMain(diag, conf, &exit_code, &got_exception);
00582         }
00583         catch (CArgException& e) {
00584             // Print USAGE and the exception error message
00585             if ( m_ArgDesc.get() ) {
00586                 x_AddDefaultArgs();
00587                 string str;
00588                 LOG_POST_X(9, m_ArgDesc->PrintUsage(str) << string(72, '='));
00589             }
00590             NCBI_REPORT_EXCEPTION_X(18, "", e);
00591             got_exception = true;
00592             exit_code = 1;
00593         }
00594 #if defined(NCBI_COMPILER_MSVC)  &&  defined(_DEBUG)
00595         // Microsoft promotes many common application errors to exceptions.
00596         // This includes occurrences such as dereference of a NULL pointer and
00597         // walking off of a dangling pointer.  The catch-all is lifted only in
00598         // debug mode to permit easy inspection of such error conditions, while
00599         // maintaining safety of production, release-mode applications.
00600         catch (...) {
00601             ERR_POST_X(10, Warning <<
00602                            "Application has thrown an exception of unknown type");
00603             throw;
00604         }
00605 #endif
00606     }
00607     else {
00608         x_TryMain(diag, conf, &exit_code, &got_exception);
00609     }
00610 
00611     if (m_ExitCodeCond == eAllExits
00612         ||  (got_exception  &&  m_ExitCodeCond == eExceptionalExits)) {
00613         _TRACE("Overriding exit code from " << exit_code
00614                << " to " << m_ExitCodeCond);
00615         exit_code = m_ExitCode;
00616     }
00617 
00618     // Application stop
00619     AppStop(exit_code);
00620 
00621     // Exit
00622     return exit_code;
00623 }
00624 
00625 
00626 void CNcbiApplication::SetEnvironment(const string& name, const string& value)
00627 {
00628     SetEnvironment().Set(name, value);
00629 }
00630 
00631 
00632 void CNcbiApplication::SetVersion(const CVersionInfo& version)
00633 {
00634     if ( s_IsApplicationStarted ) {
00635         ERR_POST_X(19, "SetVersion() should be used from constructor of " \
00636                        "CNcbiApplication derived class, see description");
00637     }
00638     m_Version->SetVersionInfo( new CVersionInfo(version) );
00639 }
00640 
00641 void CNcbiApplication::SetFullVersion( CRef<CVersion> version)
00642 {
00643     if ( s_IsApplicationStarted ) {
00644         ERR_POST_X(19, "SetFullVersion() should be used from constructor of "\
00645                        "CNcbiApplication derived class, see description");
00646     }
00647     m_Version.Reset( version );
00648 }
00649 
00650 
00651 CVersionInfo CNcbiApplication::GetVersion(void) const
00652 {
00653     return m_Version->GetVersionInfo();
00654 }
00655 
00656 const CVersion& CNcbiApplication::GetFullVersion(void) const
00657 {
00658     return *m_Version;
00659 }
00660 
00661 
00662 void CNcbiApplication::SetupArgDescriptions(CArgDescriptions* arg_desc)
00663 {
00664     m_ArgDesc.reset(arg_desc);
00665 
00666     if ( arg_desc ) {
00667         if ( !m_DisableArgDesc ) {
00668             // Add logfile and conffile arguments
00669             if ((m_HideArgs & fHideLogfile) == 0  &&
00670                 !m_ArgDesc->Exist(s_ArgLogFile + 1) ) {
00671                 m_ArgDesc->AddOptionalKey
00672                     (s_ArgLogFile+1, "File_Name",
00673                         "File to which the program log should be redirected",
00674                         CArgDescriptions::eOutputFile);
00675             }
00676             if ((m_HideArgs & fHideConffile) == 0  &&
00677                 !m_ArgDesc->Exist(s_ArgCfgFile + 1) ) {
00678                 if (m_DefaultConfig.empty()) {
00679                     m_ArgDesc->AddOptionalKey
00680                         (s_ArgCfgFile + 1, "File_Name",
00681                             "Program's configuration (registry) data file",
00682                             CArgDescriptions::eInputFile);
00683                 } else {
00684                     m_ArgDesc->AddDefaultKey
00685                         (s_ArgCfgFile + 1, "File_Name",
00686                             "Program's configuration (registry) data file",
00687                             CArgDescriptions::eInputFile,
00688                             m_DefaultConfig);
00689                 }
00690             }
00691         }
00692         m_Args.reset(arg_desc->CreateArgs(GetArguments()));
00693     } else {
00694         m_Args.reset();
00695     }
00696 }
00697 
00698 
00699 bool CNcbiApplication::SetupDiag(EAppDiagStream diag)
00700 {
00701     CDiagContext::SetupDiag(diag, 0, eDCM_Flush);
00702     return true;
00703 }
00704 
00705 
00706 bool CNcbiApplication::SetupDiag_AppSpecific(void)
00707 {
00708     CDiagContext::SetupDiag(eDS_ToStderr, 0, eDCM_Flush);
00709     return true;
00710 }
00711 
00712 
00713 bool CNcbiApplication::LoadConfig(CNcbiRegistry&        reg,
00714                                   const string*         conf,
00715                                   CNcbiRegistry::TFlags reg_flags)
00716 {
00717     string basename (m_Arguments->GetProgramBasename(eIgnoreLinks));
00718     string basename2(m_Arguments->GetProgramBasename(eFollowLinks));
00719     CMetaRegistry::SEntry entry;
00720 
00721     if ( !conf ) {
00722         return false;
00723     } else if (conf->empty()) {
00724         entry = CMetaRegistry::Load(basename, CMetaRegistry::eName_Ini, 0,
00725                                     reg_flags, &reg);
00726         if ( !entry.registry  &&  basename2 != basename ) {
00727             entry = CMetaRegistry::Load(basename2, CMetaRegistry::eName_Ini, 0,
00728                                         reg_flags, &reg);
00729         }
00730         m_DefaultConfig = CDirEntry(entry.actual_name).GetName();
00731     } else {
00732         entry = CMetaRegistry::Load(*conf, CMetaRegistry::eName_AsIs, 0,
00733                                     reg_flags, &reg);
00734     }
00735     if ( !entry.registry ) {
00736         // failed; complain as appropriate
00737         string dir;
00738         CDirEntry::SplitPath(*conf, &dir, 0, 0);
00739         if (dir.empty()) {
00740             ERR_POST_X(11, Warning <<
00741                            "Registry file of application \"" << basename
00742                            << "\" is not found");
00743         } else {
00744             NCBI_THROW(CAppException, eNoRegistry,
00745                        "Registry file \"" + *conf + "\" cannot be opened");
00746         }
00747         // still consider pulling in defaults from .ncbirc
00748         reg.IncludeNcbircIfAllowed(reg_flags);
00749         return false;
00750     } else if (entry.registry != static_cast<IRWRegistry*>(&reg)) {
00751         // should be impossible with new CMetaRegistry interface...
00752         if (&reg == m_Config  &&  reg.Empty()) {
00753             m_Config.Reset(dynamic_cast<CNcbiRegistry*>
00754                            (entry.registry.GetPointer()));
00755         } else {
00756             // copy into reg
00757             CNcbiStrstream str;
00758             entry.registry->Write(str);
00759             str.seekg(0);
00760             reg.Read(str);
00761         }
00762     }
00763     m_ConfigPath = entry.actual_name;
00764     return true;
00765 }
00766 
00767 
00768 bool CNcbiApplication::LoadConfig(CNcbiRegistry& reg,
00769                                   const string*  conf)
00770 {
00771     return LoadConfig(reg, conf, IRegistry::fWithNcbirc);
00772 }
00773 
00774 
00775 CNcbiApplication::EPreparseArgs
00776 CNcbiApplication::PreparseArgs(int                /*argc*/,
00777                                const char* const* /*argv*/)
00778 {
00779     return ePreparse_Continue;
00780 }
00781 
00782 
00783 void CNcbiApplication::DisableArgDescriptions(TDisableArgDesc disable)
00784 {
00785     m_DisableArgDesc = disable;
00786 }
00787 
00788 
00789 void CNcbiApplication::HideStdArgs(THideStdArgs hide_mask)
00790 {
00791     m_HideArgs = hide_mask;
00792 }
00793 
00794 
00795 void CNcbiApplication::SetStdioFlags(TStdioSetupFlags stdio_flags)
00796 {
00797     // do not call this function more than once
00798     // and from places other than App constructor
00799     _ASSERT(m_StdioFlags == 0);
00800     m_StdioFlags = stdio_flags;
00801 }
00802 
00803 
00804 void CNcbiApplication::x_SetupStdio(void)
00805 {
00806 #if 1//!defined(NCBI_COMPILER_GCC)  ||  NCBI_COMPILER_VERSION >= 411
00807     // CAUTION:  http://gcc.gnu.org/bugzilla/show_bug.cgi?id=26777
00808     //           fix applied Mar 29, 2006, scheduled for 4.1.1
00809     if ((m_StdioFlags & fDefault_SyncWithStdio) == 0) {
00810         // SUN WorkShop STL stream library has significant performance loss
00811         // (due to the multiple gratuitous lseeks() in std i/o)
00812         // when sync_with_stdio is TRUE (default),
00813         // so we turn off sync_with_stdio here.
00814         IOS_BASE::sync_with_stdio(false);
00815     }
00816 #endif
00817 
00818     if ((m_StdioFlags & fDefault_CinBufferSize) == 0
00819 #ifdef NCBI_OS_UNIX
00820         &&  !isatty(0)
00821 #endif
00822         ) {
00823 #if defined(NCBI_COMPILER_GCC)  &&  defined(NCBI_OS_SOLARIS)
00824 #  if NCBI_COMPILER_VERSION >= 300
00825         _ASSERT(!m_CinBuffer);
00826         // Ugly workaround for ugly interaction between g++ and Solaris C RTL
00827         const size_t kCinBufSize = 5120;
00828         m_CinBuffer = new char[kCinBufSize];
00829         cin.rdbuf()->pubsetbuf(m_CinBuffer, kCinBufSize);
00830 #  endif
00831 #endif
00832     }
00833 #ifdef NCBI_OS_MSWIN
00834     if ((m_StdioFlags & fBinaryCin) != 0) {
00835         setmode(fileno(stdin), O_BINARY);
00836     }
00837     if ((m_StdioFlags & fBinaryCout) != 0) {
00838         setmode(fileno(stdout), O_BINARY);
00839     }
00840 #endif
00841 }
00842 
00843 void CNcbiApplication::x_AddDefaultArgs(void)
00844 {
00845     if ( !m_DisableArgDesc ) {
00846         if (m_ArgDesc->IsAutoHelpEnabled()) {
00847             if ((m_HideArgs & fHideHelp) != 0) {
00848                 if (m_ArgDesc->Exist("h")) {
00849                     m_ArgDesc->Delete("h");
00850                 }
00851             }
00852         }
00853         if ((m_HideArgs & fHideFullHelp) != 0) {
00854             if (m_ArgDesc->Exist("help")) {
00855                 m_ArgDesc->Delete("help");
00856             }
00857         }
00858         if ((m_HideArgs & fHideXmlHelp) != 0) {
00859             if (m_ArgDesc->Exist("xmlhelp")) {
00860                 m_ArgDesc->Delete("xmlhelp");
00861             }
00862         }
00863         if ((m_HideArgs & fHideLogfile) == 0  &&
00864             !m_ArgDesc->Exist(s_ArgLogFile + 1)) {
00865             m_ArgDesc->AddOptionalKey
00866                 (s_ArgLogFile+1, "File_Name",
00867                     "File to which the program log should be redirected",
00868                     CArgDescriptions::eOutputFile);
00869         }
00870         if ((m_HideArgs & fHideConffile) == 0  &&
00871             !m_ArgDesc->Exist(s_ArgCfgFile + 1)) {
00872             m_ArgDesc->AddOptionalKey
00873                 (s_ArgCfgFile + 1, "File_Name",
00874                     "Program's configuration (registry) data file",
00875                     CArgDescriptions::eInputFile);
00876         }
00877         if ((m_HideArgs & fHideVersion) == 0  &&
00878             !m_ArgDesc->Exist(s_ArgVersion + 1)) {
00879             m_ArgDesc->AddFlag
00880                 (s_ArgVersion + 1,
00881                     "Print version number;  ignore other arguments");
00882         }
00883         if ((m_HideArgs & fHideFullVersion) == 0  &&
00884             !m_ArgDesc->Exist(s_ArgFullVersion + 1)) {
00885             m_ArgDesc->AddFlag
00886                 (s_ArgFullVersion + 1,
00887                     "Print extended version data;  ignore other arguments");
00888         }
00889         if ((m_HideArgs & fHideDryRun) == 0  &&
00890             !m_ArgDesc->Exist(s_ArgDryRun + 1)) {
00891             m_ArgDesc->AddFlag
00892                 (s_ArgDryRun + 1,
00893                     "Dry run the application: do nothing, only test all preconditions");
00894         }
00895     }
00896 }
00897 
00898 void CNcbiApplication::SetProgramDisplayName(const string& app_name)
00899 {
00900     m_ProgramDisplayName = app_name;
00901     // Also set app_name in the diag context
00902     if ( GetDiagContext().GetAppName().empty() ) {
00903         GetDiagContext().SetAppName(app_name);
00904     }
00905 }
00906 
00907 
00908 string CNcbiApplication::FindProgramExecutablePath
00909 (int                _DEBUG_ARG(argc),
00910  const char* const*            argv,
00911  string*                       real_path)
00912 {
00913     string ret_val;
00914 #if defined(NCBI_OS_MSWIN)  ||  defined(NCBI_OS_UNIX)
00915 
00916 #  ifdef NCBI_OS_MSWIN
00917     // MS Windows: Try more accurate method of detection
00918     // XXX - use this only for real_path?
00919     try {
00920         // Load PSAPI dynamic library -- it should exist on MS-Win NT/2000/XP
00921         CDll dll_psapi("psapi.dll", CDll::eLoadNow, CDll::eAutoUnload);
00922 
00923         // Get function entry-point from DLL
00924         BOOL  (STDMETHODCALLTYPE FAR * dllEnumProcessModules)
00925                 (HANDLE  hProcess,     // handle to process
00926                  HMODULE *lphModule,   // array of module handles
00927                  DWORD   cb,           // size of array
00928                  LPDWORD lpcbNeeded    // number of bytes required
00929                  ) = NULL;
00930 
00931         dllEnumProcessModules =
00932             dll_psapi.GetEntryPoint_Func("EnumProcessModules",
00933                                          &dllEnumProcessModules);
00934         if ( !dllEnumProcessModules ) {
00935             NCBI_THROW(CException, eUnknown, kEmptyStr);
00936         }
00937 
00938         // Find executable file in the midst of all loaded modules
00939         HANDLE  process = GetCurrentProcess();
00940         HMODULE module  = 0;
00941         DWORD   needed  = 0;
00942 
00943         // Get first module of current process (it should be .exe file)
00944         if ( dllEnumProcessModules(process,
00945                                    &module, sizeof(HMODULE), &needed) ) {
00946             if ( needed  &&  module ) {
00947                 char buf[MAX_PATH + 1];
00948                 DWORD ncount = GetModuleFileName(module, buf, MAX_PATH);
00949                 if (ncount > 0) {
00950                     ret_val.assign(buf, ncount);
00951                     if (real_path) {
00952                         *real_path = CDirEntry::NormalizePath(ret_val,
00953                                                               eFollowLinks);
00954                     }
00955                     return ret_val;
00956                 }
00957             }
00958         }
00959     }
00960     catch (CException) {
00961         ; // Just catch an all exceptions from CDll
00962     }
00963     // This method didn't work -- use standard method
00964 #  endif
00965 
00966 #  ifdef NCBI_OS_LINUX
00967     // Linux OS: Try more accurate method of detection for real_path
00968     if (real_path) {
00969         char   buf[PATH_MAX + 1];
00970         string procfile = "/proc/" + NStr::IntToString(getpid()) + "/exe";
00971         int    ncount   = readlink((procfile).c_str(), buf, PATH_MAX);
00972         if (ncount > 0) {
00973             real_path->assign(buf, ncount);
00974             real_path = 0;
00975         }
00976     }
00977 #  endif
00978 
00979     _ASSERT(argc  &&  argv);
00980     string app_path = argv[0];
00981 
00982     if ( !CDirEntry::IsAbsolutePath(app_path) ) {
00983 #  ifdef NCBI_OS_MSWIN
00984         // Add default ".exe" extention to the name of executable file
00985         // if it running without extension
00986         string dir, title, ext;
00987         CDirEntry::SplitPath(app_path, &dir, &title, &ext);
00988         if ( ext.empty() ) {
00989             app_path = CDirEntry::MakePath(dir, title, "exe");
00990         }
00991 #  endif
00992         if ( CFile(app_path).Exists() ) {
00993             // Relative path from the the current directory
00994             app_path = CDir::GetCwd() + CDirEntry::GetPathSeparator()+app_path;
00995             if ( !CFile(app_path).Exists() ) {
00996                 app_path = kEmptyStr;
00997             }
00998         } else {
00999             // Running from some path from PATH environment variable.
01000             // Try to determine that path.
01001             string env_path = GetEnvironment().Get("PATH");
01002             list<string> split_path;
01003 #  ifdef NCBI_OS_MSWIN
01004             NStr::Split(env_path, ";", split_path);
01005 #  else
01006             NStr::Split(env_path, ":", split_path);
01007 #  endif
01008             string base_name = CDirEntry(app_path).GetBase();
01009             ITERATE(list<string>, it, split_path) {
01010                 app_path = CDirEntry::MakePath(*it, base_name);
01011                 if ( CFile(app_path).Exists() ) {
01012                     break;
01013                 }
01014                 app_path = kEmptyStr;
01015             }
01016         }
01017     }
01018     ret_val = CDirEntry::NormalizePath(app_path.empty() ? argv[0] : app_path);
01019 
01020 #else  // defined (NCBI_OS_MSWIN)  ||  defined(NCBI_OS_UNIX)
01021 
01022 #  error "Unsupported platform, sorry -- please contact NCBI"
01023 #endif
01024     if (real_path) {
01025         *real_path = CDirEntry::NormalizePath(ret_val, eFollowLinks);
01026     }
01027     return ret_val;
01028 }
01029 
01030 
01031 void CNcbiApplication::x_HonorStandardSettings( IRegistry* reg)
01032 {
01033     if (reg == 0) {
01034         reg = m_Config.GetPointer();
01035         if (reg == 0)
01036             return;
01037     }
01038 
01039     // [NCBI.MEMORY_FILL]
01040     CObject::SetAllocFillMode(reg->Get("NCBI", "MEMORY_FILL"));
01041 
01042     {{
01043         CSysLog* syslog = dynamic_cast<CSysLog*>(GetDiagHandler());
01044         if (syslog) {
01045             syslog->HonorRegistrySettings(reg);
01046         }
01047     }}
01048 
01049     // Debugging features
01050 
01051     // [DEBUG.DIAG_TRACE]
01052     if ( !reg->Get("DEBUG", DIAG_TRACE).empty() ) {
01053         SetDiagTrace(eDT_Enable, eDT_Enable);
01054     }
01055 
01056     // [DEBUG.ABORT_ON_THROW]
01057     if ( !reg->Get("DEBUG", ABORT_ON_THROW).empty() ) {
01058         SetThrowTraceAbort(true);
01059     }
01060 
01061     // [DEBUG.DIAG_POST_LEVEL]
01062     string post_level = reg->Get("DEBUG", DIAG_POST_LEVEL);
01063     if ( !post_level.empty() ) {
01064         EDiagSev sev;
01065         if (CNcbiDiag::StrToSeverityLevel(post_level.c_str(), sev)) {
01066             SetDiagFixedPostLevel(sev);
01067         }
01068     }
01069 
01070     // [DEBUG.MessageFile]
01071     string msg_file = reg->Get("DEBUG", DIAG_MESSAGE_FILE);
01072     if ( !msg_file.empty() ) {
01073         CDiagErrCodeInfo* info = new CDiagErrCodeInfo();
01074         if ( !info  ||  !info->Read(msg_file) ) {
01075             if ( info ) {
01076                 delete info;
01077             }
01078             ERR_POST_X(12, Warning << "Applications message file \""
01079                            << msg_file
01080                            << "\" is not found");
01081         } else {
01082             SetDiagErrCodeInfo(info);
01083         }
01084     }
01085 
01086     // CPU and heap limitations
01087 
01088     // [NCBI.HeapSizeLimit]
01089     if ( !reg->Get("NCBI", "HeapSizeLimit").empty() ) {
01090         int heap_size_limit = reg->GetInt("NCBI", "HeapSizeLimit", 0);
01091         if (heap_size_limit < 0) {
01092             NCBI_THROW(CAppException, eLoadConfig,
01093                        "Configuration file error:  [NCBI.HeapSizeLimit] < 0");
01094         }
01095         heap_size_limit *= 1024 * 1024;
01096         if ( !SetHeapLimit(heap_size_limit) ) {
01097             ERR_POST_X(13, Warning
01098                            << "Failed to set the heap size limit to "
01099                            << heap_size_limit
01100                            << "Mb (as per the config param [NCBI.HeapSizeLimit])");
01101         }
01102     }
01103 
01104     // [NCBI.CpuTimeLimit]
01105     if ( !reg->Get("NCBI", "CpuTimeLimit").empty() ) {
01106         int cpu_time_limit = reg->GetInt("NCBI", "CpuTimeLimit", 0);
01107         if (cpu_time_limit < 0) {
01108             NCBI_THROW(CAppException, eLoadConfig,
01109                        "Configuration file error:  [NCBI.CpuTimeLimit] < 0");
01110         }
01111         if ( !SetCpuTimeLimit(cpu_time_limit) ) {
01112             ERR_POST_X(14, Warning
01113                            << "Failed to set the CPU time limit to "
01114                            << cpu_time_limit
01115                            << " sec (as per the config param [NCBI.CpuTimeLimit])");
01116         }
01117     }
01118 
01119     // TRACE and POST filters
01120 
01121     // [DIAG.TRACE_FILTER]
01122     string trace_filter = reg->Get("DIAG", "TRACE_FILTER");
01123     if ( !trace_filter.empty() )
01124         SetDiagFilter(eDiagFilter_Trace, trace_filter.c_str());
01125 
01126     // [DIAG.POST_FILTER]
01127     string post_filter = reg->Get("DIAG", "POST_FILTER");
01128     if ( !post_filter.empty() )
01129         SetDiagFilter(eDiagFilter_Post, post_filter.c_str());
01130 }
01131 
01132 
01133 void CNcbiApplication::AppStart(void)
01134 {
01135     string cmd_line = GetProgramExecutablePath();
01136     if ( m_Arguments.get() ) {
01137         if ( cmd_line.empty() ) {
01138             cmd_line = (*m_Arguments)[0];
01139         }
01140         for (SIZE_TYPE arg = 1; arg < m_Arguments->Size(); ++arg) {
01141             cmd_line += " ";
01142             cmd_line += (*m_Arguments)[arg];
01143         }
01144     }
01145 
01146     // Print application start message
01147     if ( !CDiagContext::IsSetOldPostFormat() ) {
01148         GetDiagContext().PrintStart(NStr::PrintableString(cmd_line));
01149     }
01150 }
01151 
01152 
01153 void CNcbiApplication::AppStop(int exit_code)
01154 {
01155     GetDiagContext().SetExitCode(exit_code);
01156 }
01157 
01158 
01159 void CNcbiApplication::SetExitCode(int exit_code, EExitMode when)
01160 {
01161     m_ExitCode = exit_code;
01162     m_ExitCodeCond = when;
01163 }
01164 
01165 const char* CAppException::GetErrCodeString(void) const
01166 {
01167     switch (GetErrCode()) {
01168     case eUnsetArgs:  return "eUnsetArgs";
01169     case eSetupDiag:  return "eSetupDiag";
01170     case eLoadConfig: return "eLoadConfig";
01171     case eSecond:     return "eSecond";
01172     case eNoRegistry: return "eNoRegistry";
01173     default:    return CException::GetErrCodeString();
01174     }
01175 }
01176 
01177 
01178 void CDefaultIdler::Idle(void)
01179 {
01180     DiagHandler_Reopen();
01181 }
01182 
01183 
01184 class CIdlerWrapper
01185 {
01186 public:
01187     CIdlerWrapper(void) : m_Idler(new CDefaultIdler()) {}
01188     ~CIdlerWrapper(void) {}
01189 
01190     INcbiIdler* GetIdler(EOwnership own);
01191     void SetIdler(INcbiIdler* idler, EOwnership own);
01192     void RunIdler(void);
01193 
01194 private:
01195     CMutex              m_Mutex;
01196     AutoPtr<INcbiIdler> m_Idler;
01197 };
01198 
01199 
01200 inline
01201 INcbiIdler* CIdlerWrapper::GetIdler(EOwnership own)
01202 {
01203     CMutexGuard guard(m_Mutex);
01204     m_Idler.reset(m_Idler.release(), own);
01205     return m_Idler.get();
01206 }
01207 
01208 
01209 inline
01210 void CIdlerWrapper::SetIdler(INcbiIdler* idler, EOwnership own)
01211 {
01212     CMutexGuard guard(m_Mutex);
01213     m_Idler.reset(idler, own);
01214 }
01215 
01216 
01217 inline
01218 void CIdlerWrapper::RunIdler(void)
01219 {
01220     if ( m_Idler.get() ) {
01221         CMutexGuard guard(m_Mutex);
01222         if ( m_Idler.get() ) {
01223             m_Idler->Idle();
01224         }
01225     }
01226 }
01227 
01228 
01229 CSafeStaticPtr<CIdlerWrapper> s_IdlerWrapper;
01230 
01231 INcbiIdler* GetIdler(EOwnership ownership)
01232 {
01233     return s_IdlerWrapper.Get().GetIdler(ownership);
01234 }
01235 
01236 
01237 void SetIdler(INcbiIdler* idler, EOwnership ownership)
01238 {
01239     s_IdlerWrapper.Get().SetIdler(idler, ownership);
01240 }
01241 
01242 
01243 void RunIdler(void)
01244 {
01245     s_IdlerWrapper.Get().RunIdler();
01246 }
01247 
01248 
01249 END_NCBI_SCOPE
01250 
01251 

Generated on Sun Dec 6 22:22:28 2009 for NCBI C++ ToolKit by  doxygen 1.4.6
Modified on Mon Dec 07 16:20:56 2009 by modify_doxy.py rev. 173732