1 /*  $Id: ncbiapp.cpp 620642 2020-11-25 17:54:32Z lavr $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors:  Vsevolod Sandomirskiy, Denis Vakatov
27  *
28  * File Description:
29  *   CNcbiApplication -- a generic NCBI application class
30  *   CCgiApplication  -- a NCBI CGI-application class
31  *
32  */
33 
34 #include <ncbi_pch.hpp>
35 #include <common/ncbi_source_ver.h>
36 #include <corelib/ncbiapp.hpp>
37 #undef CNcbiApplication
38 #undef CComponentVersionInfo
39 #undef CVersion
40 
41 #include <corelib/ncbifile.hpp>
42 #include <corelib/ncbi_system.hpp>
43 #include <corelib/ncbi_param.hpp>
44 #include <corelib/syslog.hpp>
45 #include <corelib/error_codes.hpp>
46 #include <corelib/ncbi_safe_static.hpp>
47 #include <corelib/request_ctx.hpp>
48 #include "ncbisys.hpp"
49 
50 #if defined(NCBI_OS_MSWIN)
51 #  include <corelib/ncbi_os_mswin.hpp>
52 #  include <corelib/ncbidll.hpp>
53 #  include <io.h>
54 #  include <fcntl.h>
55 #endif
56 
57 #if defined(NCBI_OS_UNIX)
58 #  include <unistd.h>
59 #endif
60 
61 
62 #define NCBI_USE_ERRCODE_X   Corelib_App
63 
64 
65 BEGIN_NCBI_SCOPE
66 
67 
68 /////////////////////////////////////////////////////////////////////////////
69 //  Constants
70 //
71 
72 static const char* s_ArgLogFile         = "-logfile";
73 static const char* s_ArgCfgFile         = "-conffile";
74 static const char* s_ArgVersion         = "-version";
75 static const char* s_ArgFullVersion     = "-version-full";
76 static const char* s_ArgFullVersionXml  = "-version-full-xml";
77 static const char* s_ArgFullVersionJson = "-version-full-json";
78 static const char* s_ArgDryRun          = "-dryrun";
79 
80 
81 /////////////////////////////////////////////////////////////////////////////
82 //  Global variables
83 //
84 
85 static bool s_IsApplicationStarted = false;
86 
87 
88 ///////////////////////////////////////////////////////
89 // CNcbiApplication
90 //
91 
CNcbiApplicationGuard(CNcbiApplicationAPI * app)92 CNcbiApplicationGuard::CNcbiApplicationGuard(CNcbiApplicationAPI* app) : m_App(app)
93 {
94     if (m_App) {
95         m_AppLock = make_shared<CReadLockGuard>(m_App->GetInstanceLock());
96     }
97 }
98 
99 
~CNcbiApplicationGuard(void)100 CNcbiApplicationGuard::~CNcbiApplicationGuard(void)
101 {
102 }
103 
104 
105 DEFINE_STATIC_MUTEX(s_InstanceMutex);
106 
GetInstanceMutex(void)107 SSystemMutex& CNcbiApplicationAPI::GetInstanceMutex(void)
108 {
109     return s_InstanceMutex;
110 }
111 
112 
113 static CSafeStatic<CRWLock> s_InstanceRWLock(CSafeStaticLifeSpan(CSafeStaticLifeSpan::eLifeSpan_Long, 1));
114 
GetInstanceLock(void)115 CRWLock& CNcbiApplicationAPI::GetInstanceLock(void)
116 {
117     return s_InstanceRWLock.Get();
118 }
119 
120 
121 CNcbiApplicationAPI* CNcbiApplicationAPI::m_Instance = nullptr;
122 
Instance(void)123 CNcbiApplicationAPI* CNcbiApplicationAPI::Instance(void)
124 {
125     return m_Instance;
126 }
127 
128 
InstanceGuard(void)129 CNcbiApplicationGuard CNcbiApplicationAPI::InstanceGuard(void)
130 {
131     return CNcbiApplicationGuard(m_Instance);
132 }
133 
134 
CNcbiApplicationAPI(const SBuildInfo & build_info)135 CNcbiApplicationAPI::CNcbiApplicationAPI(const SBuildInfo& build_info)
136     : m_ConfigLoaded(false),
137       m_LogFile(0),
138       m_LogOptions(0)
139 {
140     CThread::InitializeMainThreadId();
141     // Initialize UID and start timer
142     GetDiagContext().GetUID();
143     GetDiagContext().InitMessages(size_t(-1));
144     GetDiagContext().SetGlobalAppState(eDiagAppState_AppBegin);
145 
146     // Verify CPU compatibility
147     // First check. Print critical error only. See second check in x_TryInit() below.
148     {{
149         string err_message;
150         if (!VerifyCpuCompatibility(&err_message)) {
151             ERR_POST_X(22,  Critical << err_message);
152         }
153     }}
154 
155     m_DisableArgDesc = 0;
156     m_HideArgs = 0;
157     m_StdioFlags = 0;
158     m_CinBuffer = 0;
159     m_ExitCodeCond = eNoExits;
160 
161     {
162         CWriteLockGuard guard(GetInstanceLock());
163         // Register the app. instance
164         if (m_Instance) {
165             NCBI_THROW(CAppException, eSecond,
166                 "Second instance of CNcbiApplication is prohibited");
167         }
168         m_Instance = this;
169     }
170 
171     // Create empty version info
172     m_Version.Reset(new CVersionAPI(build_info));
173 
174     // Set version equal to package one if still empty (might have TeamCity build number)
175     if (m_Version->GetVersionInfo().IsAny()) {
176         auto package_info = m_Version->GetPackageVersion();
177         m_Version->SetVersionInfo(new CVersionInfo(package_info));
178     }
179 
180 #if NCBI_SC_VERSION_PROXY != 0
181     m_Version->AddComponentVersion("NCBI C++ Toolkit",
182         NCBI_SC_VERSION_PROXY, 0, NCBI_SUBVERSION_REVISION_PROXY,
183         NCBI_TEAMCITY_PROJECT_NAME_PROXY, NCBI_APP_SBUILDINFO_DEFAULT());
184 #endif
185     // Create empty application arguments & name
186     m_Arguments.reset(new CNcbiArguments(0,0));
187 
188     // Create empty application environment
189     m_Environ.reset(new CNcbiEnvironment);
190 
191     // Create an empty registry
192     m_Config.Reset(new CNcbiRegistry);
193 
194     m_DryRun = false;
195 }
196 
ExecuteOnExitActions()197 void CNcbiApplicationAPI::ExecuteOnExitActions()
198 {
199     m_OnExitActions.ExecuteActions();
200 }
201 
202 
~CNcbiApplicationAPI(void)203 CNcbiApplicationAPI::~CNcbiApplicationAPI(void)
204 {
205     CThread::sm_IsExiting = true;
206 
207     // Execute exit actions before waiting for all threads to stop.
208     // NOTE: The exit actions may already be executed by higher-level
209     //       destructors. This is a final fail-safe place for this.
210     ExecuteOnExitActions();
211 
212 #if defined(NCBI_THREADS)
213     CThread::WaitForAllThreads();
214 #endif
215 
216     {
217         CWriteLockGuard guard(GetInstanceLock());
218         m_Instance = 0;
219     }
220     FlushDiag(0, true);
221     if (m_CinBuffer) {
222         delete [] m_CinBuffer;
223     }
224 
225 #if defined(NCBI_COMPILER_WORKSHOP)
226     // At least under these conditions:
227     //  1) WorkShop 5.5 on Solaris 10/SPARC, Release64MT,    and
228     //  2) when IOS_BASE::sync_with_stdio(false) is called,  and
229     //  3) the contents of 'cout' is not flushed
230     // some applications crash on exit() while apparently trying to
231     // flush 'cout' and getting confused by its own guts, with error:
232     //   "*** libc thread failure: _thread_setschedparam_main() fails"
233     //
234     // This forced pre-flush trick seems to fix the problem.
235     NcbiCout.flush();
236 #endif
237 }
238 
239 
Instance(void)240 CNcbiApplication* CNcbiApplication::Instance(void)
241 {
242     return dynamic_cast<CNcbiApplication*>(CNcbiApplicationAPI::Instance());
243 }
244 
245 
CNcbiApplication(const SBuildInfo & build_info)246 CNcbiApplication::CNcbiApplication(const SBuildInfo& build_info)
247     : CNcbiApplicationAPI(build_info)
248 {
249 }
250 
251 
~CNcbiApplication()252 CNcbiApplication::~CNcbiApplication()
253 {
254     // This earlier execution of the actions allows a safe use of
255     // CNcbiApplication::Instance() from the exit action functions. Instance()
256     // can return NULL pointer if called as part of CNcbiApplicationAPI dtor
257     // when the CNcbiApplication dtor already finished.
258     ExecuteOnExitActions();
259 }
260 
261 
Init(void)262 void CNcbiApplicationAPI::Init(void)
263 {
264     return;
265 }
266 
267 
DryRun(void)268 int CNcbiApplicationAPI::DryRun(void)
269 {
270     ERR_POST_X(1, Info << "DryRun: default implementation does nothing");
271     return 0;
272 }
273 
274 
Exit(void)275 void CNcbiApplicationAPI::Exit(void)
276 {
277     return;
278 }
279 
280 
GetArgs(void) const281 const CArgs& CNcbiApplicationAPI::GetArgs(void) const
282 {
283     if ( !m_Args.get() ) {
284         NCBI_THROW(CAppException, eUnsetArgs,
285                    "Command-line argument description is not found");
286     }
287     return *m_Args;
288 }
289 
290 
FlushDiag(CNcbiOstream * os,bool)291 SIZE_TYPE CNcbiApplicationAPI::FlushDiag(CNcbiOstream* os, bool /*close_diag*/)
292 {
293     if ( os ) {
294         SetDiagStream(os, true, 0, 0, "STREAM");
295     }
296     GetDiagContext().FlushMessages(*GetDiagHandler());
297     GetDiagContext().DiscardMessages();
298     return 0;
299 }
300 
301 
302 #if defined(NCBI_OS_DARWIN)
s_MacArgMunging(CNcbiApplicationAPI & app,int * argcPtr,const char * const ** argvPtr,const string & exepath)303 static void s_MacArgMunging(CNcbiApplicationAPI&   app,
304                             int*                argcPtr,
305                             const char* const** argvPtr,
306                             const string&       exepath)
307 {
308 
309     // Sometimes on Mac there will be an argument -psn which
310     // will be followed by the Process Serial Number, e.g. -psn_0_13107201
311     // this is in situations where the application could have no other
312     // arguments like when it is double clicked.
313     // This will mess up argument processing later, so get rid of it.
314     static const char* s_ArgMacPsn = "-psn_";
315 
316     if (*argcPtr == 2  &&
317         NStr::strncmp((*argvPtr)[1], s_ArgMacPsn, strlen(s_ArgMacPsn)) == 0) {
318         --*argcPtr;
319     }
320 
321     if (*argcPtr > 1)
322         return;
323 
324     // Have no arguments from the operating system -- so use the '.args' file
325 
326     // Open the args file.
327     string exedir;
328     CDir::SplitPath(exepath, &exedir);
329     string args_fname = exedir + app.GetProgramDisplayName() + ".args";
330     CNcbiIfstream in(args_fname.c_str());
331 
332     if ( !in.good() ) {
333         ERR_POST_X(2, Info << "Mac arguments file not found: " << args_fname);
334         return;
335     }
336 
337     vector<string> v;
338 
339     // remember or fake the executable name.
340     if (*argcPtr > 0) {
341         v.push_back((*argvPtr)[0]); // preserve the original argv[0].
342     } else {
343         v.push_back(exepath);
344     }
345 
346     // grab the rest of the arguments from the file.
347     // arguments are separated by whitespace. Can be on
348     // more than one line.
349     string arg;
350     while (in >> arg) {
351         v.push_back(arg);
352     }
353 
354     // stash them away in the standard argc and argv places.
355     *argcPtr = v.size();
356 
357     char** argv =  new char*[v.size()];
358     int c = 0;
359     ITERATE(vector<string>, vp, v) {
360         argv[c++] = strdup(vp->c_str());
361     }
362     *argvPtr = argv;
363 }
364 #endif  /* NCBI_OS_DARWIN */
365 
366 
367 NCBI_PARAM_DECL(bool, Debug, Catch_Unhandled_Exceptions);
368 NCBI_PARAM_DEF_EX(bool, Debug, Catch_Unhandled_Exceptions, true,
369                   eParam_NoThread, DEBUG_CATCH_UNHANDLED_EXCEPTIONS);
370 typedef NCBI_PARAM_TYPE(Debug, Catch_Unhandled_Exceptions) TParamCatchExceptions;
371 
s_HandleExceptions(void)372 bool s_HandleExceptions(void)
373 {
374     return TParamCatchExceptions::GetDefault();
375 }
376 
377 
378 NCBI_PARAM_DECL(bool, NCBI, TerminateOnCpuIncompatibility);
379 NCBI_PARAM_DEF_EX(bool, NCBI, TerminateOnCpuIncompatibility, false,
380                   eParam_NoThread, NCBI_CONFIG__TERMINATE_ON_CPU_INCOMPATIBILITY);
381 
382 
x_TryInit(EAppDiagStream diag,const char * conf)383 void CNcbiApplicationAPI::x_TryInit(EAppDiagStream diag, const char* conf)
384 {
385     // Load registry from the config file
386     if ( conf ) {
387         string x_conf(conf);
388         LoadConfig(*m_Config, &x_conf);
389     } else {
390         LoadConfig(*m_Config, NULL);
391     }
392     m_ConfigLoaded = true;
393 
394     CDiagContext::SetupDiag(diag, m_Config, eDCM_Flush, m_LogFile);
395     CDiagContext::x_FinalizeSetupDiag();
396 
397     // Setup the standard features from the config file.
398     // Don't call till after LoadConfig()
399     // NOTE: this will override environment variables,
400     // except DIAG_POST_LEVEL which is Set*Fixed*.
401     x_HonorStandardSettings();
402 
403     // Application start
404     AppStart();
405 
406     // Verify CPU compatibility
407     // Second check. Print error message and allow to terminate program depends on configuration parameters.
408     // Also, see first check in CNcbiApplicationAPI() constructor.
409     {{
410         string err_message;
411         if (!VerifyCpuCompatibility(&err_message)) {
412             bool fatal = NCBI_PARAM_TYPE(NCBI, TerminateOnCpuIncompatibility)::GetDefault();
413             ERR_POST_X(22, (fatal ? Fatal : Critical) << err_message);
414         }
415     }}
416 
417     // Do init
418 #if (defined(NCBI_COMPILER_ICC) && NCBI_COMPILER_VERSION < 900)
419     // ICC 8.0 have an optimization bug in exceptions handling,
420     // so workaround it here
421     try {
422         Init();
423     }
424     catch (const CArgHelpException&) {
425         throw;
426     }
427 #else
428     Init();
429 #endif
430 
431     // If the app still has no arg description - provide default one
432     if (!m_DisableArgDesc  &&  !m_ArgDesc.get()) {
433         unique_ptr<CArgDescriptions> arg_desc(new CArgDescriptions);
434         arg_desc->SetUsageContext
435             (GetArguments().GetProgramBasename(),
436              "This program has no mandatory arguments");
437         SetupArgDescriptions(arg_desc.release());
438     }
439 }
440 
441 // Macro to define a logging parameter
442 #define NCBI_LOG_PARAM(type,Name,NAME)             \
443     NCBI_PARAM_DECL  (type, Log, LogApp ## Name);  \
444     NCBI_PARAM_DEF_EX(type, Log, LogApp ## Name, false, eParam_NoThread, DIAG_LOG_APP_ ## NAME); \
445     typedef NCBI_PARAM_TYPE(Log, LogApp ## Name) TLogApp ## Name;
446 
447 NCBI_LOG_PARAM(bool, Environment,        ENVIRONMENT)
448 NCBI_LOG_PARAM(bool, EnvironmentOnStop,  ENVIRONMENT_ON_STOP)
449 NCBI_LOG_PARAM(bool, Registry,           REGISTRY)
450 NCBI_LOG_PARAM(bool, RegistryOnStop,     REGISTRY_ON_STOP)
451 NCBI_LOG_PARAM(bool, Arguments,          ARGUMENTS)
452 NCBI_LOG_PARAM(bool, Path,               PATH)
453 NCBI_LOG_PARAM(bool, RunContext,         RUN_CONTEXT)
454 NCBI_LOG_PARAM(bool, ResUsageOnStop,     RESUSAGE_ON_STOP)
455 
456 
457 enum ELogOptionsEvent {
458     eStartEvent = 0x01, ///< right before AppMain()
459     eStopEvent  = 0x02, ///< right after AppMain()
460     eOtherEvent = 0x03  ///< any case is fine
461 };
462 
463 
464 /// Flags to switch what to log
465 enum ELogOptions {
466     fLogAppEnvironment      = 0x01, ///< log app environment on app start
467     fLogAppEnvironmentStop  = 0x02, ///< log app environment on app stop
468     fLogAppRegistry         = 0x04, ///< log app registry on app start
469     fLogAppRegistryStop     = 0x08, ///< log app registry on app stop
470     fLogAppArguments        = 0x10, ///< log app arguments
471     fLogAppPath             = 0x20, ///< log app executable path
472     fLogAppResUsageStop     = 0x40, ///< log resource usage on app stop
473 };
474 
475 
x_ReadLogOptions()476 void CNcbiApplicationAPI::x_ReadLogOptions()
477 {
478     // Log all
479     if ( TLogAppRunContext::GetDefault() ) {
480         m_LogOptions = 0x7f; // all on
481         return;
482     }
483 
484     // Log registry
485     m_LogOptions |= TLogAppRegistry::GetDefault() ? fLogAppRegistry : 0;
486     m_LogOptions |= TLogAppRegistryOnStop::GetDefault() ? fLogAppRegistryStop : 0;
487 
488     // Log environment
489     m_LogOptions |= TLogAppEnvironment::GetDefault() ? fLogAppEnvironment : 0;
490     m_LogOptions |= TLogAppEnvironmentOnStop::GetDefault() ? fLogAppEnvironmentStop : 0;
491 
492     // Log arguments
493     m_LogOptions |= TLogAppArguments::GetDefault() ? fLogAppArguments : 0;
494 
495     // Log path
496     m_LogOptions |= TLogAppPath::GetDefault() ? fLogAppPath : 0;
497 
498     // Log resources usage
499     m_LogOptions |= TLogAppResUsageOnStop::GetDefault() ? fLogAppResUsageStop : 0;
500 }
501 
502 
s_RoundResUsageSize(Uint8 value_in_bytes,string & suffix,Uint8 & value)503 void s_RoundResUsageSize(Uint8 value_in_bytes, string& suffix, Uint8& value)
504 {
505     const Uint8 limit = 1000;
506 
507     // KB by default
508     suffix = "_KB";
509     value = value_in_bytes / 1024;
510 
511     // Round to MB if value is too big
512     if (value / 1024 > limit) {
513         suffix = "_MB";
514         value /= 1024;
515     }
516 }
517 
518 #define RES_SIZE_USAGE(name, value_in_bytes) \
519     { \
520         string suffix; \
521         Uint8  value; \
522         s_RoundResUsageSize(value_in_bytes, suffix, value); \
523         extra.Print(name + suffix, value); \
524     }
525 
526 #define RES_TIME_USAGE(name, value) \
527     if (value >= 0 ) \
528         extra.Print(name, (Uint8)value)
529 
530 
x_LogOptions(int event)531 void CNcbiApplicationAPI::x_LogOptions(int /*ELogOptionsEvent*/ event)
532 {
533     const bool start = (event & eStartEvent) != 0;
534     const bool stop  = (event & eStopEvent)  != 0;
535 
536     // Print environment values
537     if ( (m_LogOptions & fLogAppEnvironment      &&  start) ||
538          (m_LogOptions & fLogAppEnvironmentStop  &&  stop) ) {
539         CDiagContext_Extra extra = GetDiagContext().Extra();
540         extra.Print("LogAppEnvironment", "true");
541         list<string> env_keys;
542         const CNcbiEnvironment& env = GetEnvironment();
543         env.Enumerate(env_keys);
544         ITERATE(list<string>, it, env_keys) {
545             const string& val = env.Get(*it);
546             extra.Print(*it, val);
547         }
548     }
549 
550     // Print registry values
551     if ( (m_LogOptions & fLogAppRegistry      &&  start) ||
552          (m_LogOptions & fLogAppRegistryStop  &&  stop) ) {
553         CDiagContext_Extra extra = GetDiagContext().Extra();
554         extra.Print("LogAppRegistry", "true");
555         list<string> reg_sections;
556         const CNcbiRegistry& reg = GetConfig();
557         reg.EnumerateSections(&reg_sections);
558         ITERATE(list<string>, it, reg_sections) {
559             string section, name;
560             list<string> section_entries;
561             reg.EnumerateEntries(*it, &section_entries);
562             ITERATE(list<string>, it_entry, section_entries) {
563                 const string& val = reg.Get(*it, *it_entry);
564                 string path = "[" + *it + "]" + *it_entry;
565                 extra.Print(path, val);
566             }
567         }
568     }
569 
570     // Print arguments
571     if ( m_LogOptions & fLogAppArguments  &&  start) {
572         CDiagContext_Extra extra = GetDiagContext().Extra();
573         extra.Print("LogAppArguments", "true");
574         string args_str;
575         extra.Print("Arguments", GetArgs().Print(args_str));
576     }
577 
578     // Print app path
579     if ( m_LogOptions & fLogAppPath  &&  start) {
580         CDiagContext_Extra extra = GetDiagContext().Extra();
581         extra.Print("LogAppPath", "true");
582         extra.Print("Path", GetProgramExecutablePath());
583     }
584 
585     // Print resource usage
586     if ( m_LogOptions & fLogAppResUsageStop  &&  stop) {
587         CDiagContext_Extra extra = GetDiagContext().Extra();
588         extra.Print("LogAppResUsage", "true");
589         // Memory usage
590         CProcess::SMemoryUsage mem_usage;
591         if ( CCurrentProcess::GetMemoryUsage(mem_usage) ) {
592             RES_SIZE_USAGE("mem_total",      mem_usage.total        );
593             RES_SIZE_USAGE("mem_total_peak", mem_usage.total_peak   );
594             RES_SIZE_USAGE("rss_mem",        mem_usage.resident     );
595             RES_SIZE_USAGE("rss_peak_mem",   mem_usage.resident_peak);
596             RES_SIZE_USAGE("shared.mem",     mem_usage.shared       );
597             RES_SIZE_USAGE("data.mem",       mem_usage.data         );
598             RES_SIZE_USAGE("stack.mem",      mem_usage.stack        );
599         }
600         // CPU time usage
601         double real, user, sys;
602         if ( CCurrentProcess::GetTimes(&real, &user, &sys, CProcess::eProcess) ) {
603             RES_TIME_USAGE("real.proc.cpu", real);
604             RES_TIME_USAGE("user.proc.cpu", user);
605             RES_TIME_USAGE("sys.proc.cpu",  sys);
606         }
607         if ( CCurrentProcess::GetTimes(&real, &user, &sys, CProcess::eChildren) ) {
608             RES_TIME_USAGE("user.child.cpu", user);
609             RES_TIME_USAGE("sys.child.cpu",  sys);
610         }
611         if ( CCurrentProcess::GetTimes(&real, &user, &sys, CProcess::eThread) ) {
612             RES_TIME_USAGE("user.thread.cpu", user);
613             RES_TIME_USAGE("sys.thread.cpu",  sys);
614         }
615     }
616 }
617 
618 
x_TryMain(EAppDiagStream diag,const char * conf,int * exit_code,bool * got_exception)619 void CNcbiApplicationAPI::x_TryMain(EAppDiagStream diag,
620                                  const char*    conf,
621                                  int*           exit_code,
622                                  bool*          got_exception)
623 {
624     // Initialize the application
625     try {
626         if ( s_HandleExceptions() ) {
627             try {
628                 x_TryInit(diag, conf);
629             }
630             catch (const CArgHelpException&) {
631                 // This exceptions will be caught later regardless of the
632                 // handle-exceptions flag.
633                 throw;
634             }
635             catch (const CArgException&) {
636 //                NCBI_RETHROW_SAME(e, "Application's initialization failed");
637                 throw;
638             }
639             catch (const CException& e) {
640                 NCBI_REPORT_EXCEPTION_X(15,
641                                         "Application's initialization failed", e);
642                 *got_exception = true;
643                 *exit_code = 2;
644             }
645             catch (const exception& e) {
646                 ERR_POST_X(6, "Application's initialization failed: " << e.what());
647                 *got_exception = true;
648                 *exit_code = 2;
649             }
650         }
651         else {
652             x_TryInit(diag, conf);
653         }
654     }
655     catch (const CArgHelpException& e) {
656         x_AddDefaultArgs();
657         // Print USAGE
658         if (e.GetErrCode() == CArgHelpException::eHelpXml) {
659             m_ArgDesc->PrintUsageXml(cout);
660         } else {
661             string str;
662             m_ArgDesc->PrintUsage
663                 (str, e.GetErrCode() == CArgHelpException::eHelpFull);
664             cout << str;
665         }
666         *exit_code = e.GetErrCode() == CArgHelpException::eHelpErr ? 2 : 0;
667     }
668     x_ReadLogOptions();
669     x_LogOptions(eStartEvent);
670     // Run application
671     if (*exit_code == 1) {
672         GetDiagContext().SetGlobalAppState(eDiagAppState_AppRun);
673         if ( s_HandleExceptions() ) {
674             try {
675                 *exit_code = m_DryRun ? DryRun() : Run();
676             }
677             catch (CArgException& e) {
678                 NCBI_RETHROW_SAME(e, "Application's execution failed");
679             }
680             catch (const CException& e) {
681                 CRef<CRequestContext> cur_ctx(&GetDiagContext().GetRequestContext());
682                 if (cur_ctx != &e.GetRequestContext()) {
683                     GetDiagContext().SetRequestContext(&e.GetRequestContext());
684                     NCBI_REPORT_EXCEPTION_X(16,
685                                             "CException thrown", e);
686                     GetDiagContext().SetRequestContext(cur_ctx);
687                 }
688                 NCBI_REPORT_EXCEPTION_X(16,
689                                         "Application's execution failed", e);
690                 *got_exception = true;
691                 *exit_code = 3;
692             }
693             catch (const exception& e) {
694                 ERR_POST_X(7, "Application's execution failed: " << e.what());
695                 *got_exception = true;
696                 *exit_code = 3;
697             }
698         }
699         else {
700             *exit_code = m_DryRun ? DryRun() : Run();
701         }
702     }
703     x_LogOptions(eStopEvent);
704     GetDiagContext().SetGlobalAppState(eDiagAppState_AppEnd);
705 
706     // Close application
707     if ( s_HandleExceptions() ) {
708         try {
709             Exit();
710         }
711         catch (CArgException& e) {
712             NCBI_RETHROW_SAME(e, "Application's cleanup failed");
713         }
714         catch (const CException& e) {
715             NCBI_REPORT_EXCEPTION_X(17, "Application's cleanup failed", e);
716             *got_exception = true;
717         }
718         catch (const exception& e) {
719             ERR_POST_X(8, "Application's cleanup failed: "<< e.what());
720             *got_exception = true;
721         }
722     }
723     else {
724         Exit();
725     }
726 }
727 
728 #if defined(NCBI_OS_MSWIN) && defined(_UNICODE)
729 static
s_Create_ArgsOrEnvW(vector<string> & storage,AutoArray<const char * > & pointers,const TXChar * const * begin)730 void s_Create_ArgsOrEnvW(
731     vector<string>& storage,
732     AutoArray<const char*>& pointers,
733     const TXChar* const* begin)
734 {
735     const TXChar* const* arg = begin;
736     size_t count = 0;
737     while( *(arg++) )
738         ++count;
739 
740     const char** args = new const char*[count+1];
741     if ( !args ) {
742         NCBI_THROW(CCoreException, eNullPtr, kEmptyStr);
743     }
744     pointers = args;
745 
746     arg = begin;
747     size_t i=0;
748     for (i=0; i<count; ++i) {
749         storage.push_back( _T_STDSTRING( *(arg++) ) );
750     }
751 
752     for (i=0; i < storage.size(); ++i) {
753         args[i] = storage[i].c_str();
754     }
755     args[i] = NULL;
756 }
757 
AppMain(int argc,const TXChar * const * argv,const TXChar * const * envp,EAppDiagStream diag,const TXChar * conf,const TXString & name)758 int CNcbiApplicationAPI::AppMain
759 (int                  argc,
760  const TXChar* const* argv,
761  const TXChar* const* envp,
762  EAppDiagStream       diag,
763  const TXChar*        conf,
764  const TXString&      name)
765 {
766     vector< string> argv_storage;
767     AutoArray<const char*> argv_pointers;
768     if (argv) {
769         s_Create_ArgsOrEnvW(argv_storage, argv_pointers, argv);
770     }
771 
772     vector< string> envp_storage;
773     AutoArray<const char*> envp_pointers;
774     if (envp) {
775         s_Create_ArgsOrEnvW(envp_storage, envp_pointers, envp);
776     }
777 
778     return AppMain(argc,
779         argv == NULL ? NULL : argv_pointers.get(),
780         envp == NULL ? NULL : envp_pointers.get(),
781         diag,
782         conf == NULL ? NULL : (conf == NcbiEmptyXCStr ? NcbiEmptyCStr : _T_CSTRING(conf)),
783         name == NcbiEmptyXString ? NcbiEmptyString : _T_STDSTRING(name));
784 }
785 #endif
786 
AppMain(int argc,const char * const * argv,const char * const * envp,EAppDiagStream diag,const char * conf,const string & name)787 int CNcbiApplicationAPI::AppMain
788 (int                argc,
789  const char* const* argv,
790  const char* const* envp,
791  EAppDiagStream     diag,
792  const char*        conf,
793  const string&      name)
794 {
795     if (conf) {
796         m_DefaultConfig = conf;
797     }
798     x_SetupStdio();
799 
800     // Check if logfile is set in the args.
801     m_LogFile = 0;
802     if (!m_DisableArgDesc && argc > 1  &&  argv  &&  diag != eDS_User) {
803         for (int i = 1;  i < argc;  i++) {
804             if ( !argv[i] ) {
805                 continue;
806             }
807             if ( NStr::strcmp(argv[i], s_ArgLogFile) == 0 ) {
808                 if (!argv[++i]) {
809                     continue;
810                 }
811                 m_LogFile = argv[i];
812             } else if (NStr::StartsWith(argv[i], s_ArgLogFile)) {
813                 const char *a = argv[i] + strlen(s_ArgLogFile);
814                 if (*a == '=') {
815                     m_LogFile = ++a;
816                 }
817             }
818         }
819     }
820     // Setup logging as soon as possible.
821     // Setup for diagnostics
822     try {
823         CDiagContext::SetupDiag(diag, 0, eDCM_NoChange, m_LogFile);
824     } catch (const CException& e) {
825         NCBI_RETHROW(e, CAppException, eSetupDiag,
826                      "Application diagnostic stream's setup failed");
827     } catch (const exception& e) {
828         NCBI_THROW(CAppException, eSetupDiag,
829                    "Application diagnostic stream's setup failed: " +
830                    string(e.what()));
831     }
832 
833     // Get program executable's name & path.
834     string exepath = FindProgramExecutablePath(argc, argv, &m_RealExePath);
835     m_ExePath = exepath;
836 
837     // Get program display name
838     string appname = name;
839     if (appname.empty()) {
840         if (!exepath.empty()) {
841             CDirEntry::SplitPath(exepath, NULL, &appname);
842         } else if (argc > 0  &&  argv[0] != NULL  &&  *argv[0] != '\0') {
843             CDirEntry::SplitPath(argv[0], NULL, &appname);
844         } else {
845             appname = "ncbi";
846         }
847     }
848     if ( m_ProgramDisplayName.empty() ) {
849         SetProgramDisplayName(appname);
850     }
851 
852     // Make sure we have something as our 'real' executable's name.
853     // though if it does not contain a full path it won't be much use.
854     if ( exepath.empty() ) {
855         ERR_POST_X(3, Warning
856                       << "Warning:  Could not determine this application's "
857                       "file name and location.  Using \""
858                       << appname << "\" instead.\n"
859                       "Please fix FindProgramExecutablePath() on this platform.");
860         exepath = appname;
861     }
862 
863 #if defined(NCBI_OS_DARWIN)
864     // We do not know standard way of passing arguments to C++ program on Mac,
865     // so we will read arguments from special file having extension ".args"
866     // and name equal to display name of program (name argument of AppMain).
867     s_MacArgMunging(*this, &argc, &argv, exepath);
868 #endif
869 
870     CDiagContext& diag_context = GetDiagContext();
871 
872     // Preparse command line
873     if (PreparseArgs(argc, argv) == ePreparse_Exit) {
874         diag_context.DiscardMessages();
875         return 0;
876     }
877 
878     // Check command line for presence special arguments
879     // "-logfile", "-conffile", "-version"
880     if (!m_DisableArgDesc && argc > 1  &&  argv) {
881         const char** v = new const char*[argc];
882         v[0] = argv[0];
883         int real_arg_index = 1;
884         for (int i = 1;  i < argc;  i++) {
885             if ( !argv[i] ) {
886                 continue;
887             }
888             // Log file - ignore if diag is eDS_User - the user wants to
889             // take care about logging.
890             if ( diag != eDS_User  &&
891                 NStr::strcmp(argv[i], s_ArgLogFile) == 0 ) {
892                 if (!argv[++i]) {
893                     continue;
894                 }
895                 v[real_arg_index++] = argv[i - 1];
896                 v[real_arg_index++] = argv[i];
897                 // Configuration file
898             } else if ( NStr::strcmp(argv[i], s_ArgCfgFile) == 0 ) {
899                 if (!argv[++i]) {
900                     continue;
901                 }
902                 v[real_arg_index++] = argv[i - 1];
903                 v[real_arg_index++] = argv[i];
904                 conf = argv[i];
905 
906             }
907             else if (NStr::StartsWith(argv[i], s_ArgCfgFile)) {
908                 v[real_arg_index++] = argv[i];
909                 const char* a = argv[i] + strlen(s_ArgCfgFile);
910                 if (*a == '=') {
911                     conf = ++a;
912                 }
913 
914                 // Version
915             } else if ( NStr::strcmp(argv[i], s_ArgVersion) == 0 ) {
916                 delete[] v;
917                 // Print VERSION
918                 cout << GetFullVersion().Print( appname, CVersionAPI::fVersionInfo | CVersionAPI::fPackageShort );
919                 diag_context.DiscardMessages();
920                 return 0;
921 
922                 // Full version
923             } else if ( NStr::strcmp(argv[i], s_ArgFullVersion) == 0 ) {
924                 delete[] v;
925                 // Print full VERSION
926                 cout << GetFullVersion().Print( appname );
927                 diag_context.DiscardMessages();
928                 return 0;
929             } else if ( NStr::strcmp(argv[i], s_ArgFullVersionXml) == 0 ) {
930                 delete[] v;
931                 // Print full VERSION in XML format
932                 cout << GetFullVersion().PrintXml( appname );
933                 diag_context.DiscardMessages();
934                 return 0;
935             } else if ( NStr::strcmp(argv[i], s_ArgFullVersionJson) == 0 ) {
936                 delete[] v;
937                 // Print full VERSION in JSON format
938                 cout << GetFullVersion().PrintJson( appname );
939                 diag_context.DiscardMessages();
940                 return 0;
941 
942                 // Dry run
943             } else if ( NStr::strcmp(argv[i], s_ArgDryRun) == 0 ) {
944                 m_DryRun = true;
945 
946                 // Save real argument
947             } else {
948                 v[real_arg_index++] = argv[i];
949             }
950         }
951         if (real_arg_index == argc ) {
952             delete[] v;
953         } else {
954             argc = real_arg_index;
955             argv = v;
956         }
957     }
958 
959     // Reset command-line args and application name
960     m_Arguments->Reset(argc, argv, exepath, m_RealExePath);
961 
962     // Reset application environment
963     m_Environ->Reset(envp);
964 
965     // Setup some debugging features from environment variables.
966     if ( !m_Environ->Get(DIAG_TRACE).empty() ) {
967         SetDiagTrace(eDT_Enable, eDT_Enable);
968     }
969     string post_level = m_Environ->Get(DIAG_POST_LEVEL);
970     if ( !post_level.empty() ) {
971         EDiagSev sev;
972         if (CNcbiDiag::StrToSeverityLevel(post_level.c_str(), sev)) {
973             SetDiagFixedPostLevel(sev);
974         }
975     }
976     if ( !m_Environ->Get(ABORT_ON_THROW).empty() ) {
977         SetThrowTraceAbort(true);
978     }
979 
980     // Clear registry content
981     m_Config->Clear();
982 
983     // Call:  Init() + Run() + Exit()
984     int exit_code = 1;
985     bool got_exception = false;
986     s_IsApplicationStarted = true;
987 
988     try {
989         if ( s_HandleExceptions() ) {
990             try {
991                 x_TryMain(diag, conf, &exit_code, &got_exception);
992             }
993             catch (const CArgException&) {
994                 // This exceptions will be caught later regardless of the
995                 // handle-exceptions flag.
996                 throw;
997             }
998 #if defined(NCBI_COMPILER_MSVC)  &&  defined(_DEBUG)
999             // Microsoft promotes many common application errors to exceptions.
1000             // This includes occurrences such as dereference of a NULL pointer and
1001             // walking off of a dangling pointer.  The catch-all is lifted only in
1002             // debug mode to permit easy inspection of such error conditions, while
1003             // maintaining safety of production, release-mode applications.
1004             catch (...) {
1005                 ERR_POST_X(10, Warning <<
1006                                "Application has thrown an exception of unknown type");
1007                 throw;
1008             }
1009 #endif
1010         }
1011         else {
1012 #ifdef NCBI_OS_MSWIN
1013             if ( !IsDebuggerPresent() ) {
1014                 SuppressSystemMessageBox(fSuppress_Exception);
1015             }
1016 #endif
1017             x_TryMain(diag, conf, &exit_code, &got_exception);
1018         }
1019     }
1020     catch (const CArgException& e) {
1021         // Print USAGE and the exception error message
1022         if ( e.GetErrCode() != CArgException::eNoValue &&  m_ArgDesc.get() ) {
1023             x_AddDefaultArgs();
1024             string str;
1025             if ( !m_ArgDesc->IsSetMiscFlag(CArgDescriptions::fNoUsage) ) {
1026                 m_ArgDesc->PrintUsage(str);
1027                 cerr << str;
1028             }
1029             if ( !m_ArgDesc->IsSetMiscFlag(CArgDescriptions::fDupErrToCerr) ) {
1030                 CStreamDiagHandler* errh =
1031                     dynamic_cast<CStreamDiagHandler*>(GetDiagHandler());
1032                 if (!errh  ||  errh->GetStream() != &cerr) {
1033                     cerr << "Error in command-line arguments. "
1034                         "See error logs for more details." << endl;
1035                 }
1036             }
1037             else {
1038                 cerr << "Error in command-line arguments." << endl;
1039                 cerr << e.what() << endl;
1040             }
1041             cerr << string(72, '=') << endl << endl;
1042         }
1043         SetDiagPostAllFlags(eDPF_Severity);
1044         NCBI_REPORT_EXCEPTION_X(18, "", e);
1045         got_exception = true;
1046         exit_code = 1;
1047     }
1048 
1049     if (!diag_context.IsSetExitCode()) {
1050         diag_context.SetExitCode(exit_code);
1051     }
1052 
1053     if (m_ExitCodeCond == eAllExits
1054         ||  (got_exception  &&  m_ExitCodeCond == eExceptionalExits)) {
1055         _TRACE("Overriding exit code from " << exit_code
1056                << " to " << m_ExitCode);
1057         exit_code = m_ExitCode;
1058     }
1059 
1060     // Application stop
1061     AppStop(exit_code);
1062 
1063     if ((m_AppFlags & fSkipSafeStaticDestroy) == 0) {
1064         // Destroy short-lived statics
1065         CSafeStaticGuard::Destroy(CSafeStaticLifeSpan::eLifeLevel_AppMain);
1066     }
1067 
1068     // Exit
1069     return exit_code;
1070 }
1071 
1072 
SetEnvironment(const string & name,const string & value)1073 void CNcbiApplicationAPI::SetEnvironment(const string& name, const string& value)
1074 {
1075     SetEnvironment().Set(name, value);
1076 }
1077 
1078 #if 0
1079 void CNcbiApplicationAPI::SetVersionByBuild(int major)
1080 {
1081     SetVersion(major, NStr::StringToInt(m_Version->GetBuildInfo().GetExtraValue(SBuildInfo::eStableComponentsVersion, "0")));
1082 }
1083 
1084 void CNcbiApplicationAPI::SetVersion(int major, int minor)
1085 {
1086     SetVersion(major, minor, NStr::StringToInt(m_Version->GetBuildInfo().GetExtraValue(SBuildInfo::eTeamCityBuildNumber, "0")));
1087 }
1088 
1089 void CNcbiApplicationAPI::SetVersion(int major, int minor, int patch)
1090 {
1091     m_Version->SetVersionInfo(major, minor, patch,
1092         m_Version->GetBuildInfo().GetExtraValue(SBuildInfo::eTeamCityProjectName));
1093 }
1094 #else
SetVersionByBuild(int major)1095 void CNcbiApplicationAPI::SetVersionByBuild(int major)
1096 {
1097     m_Version->SetVersionInfo(major, NCBI_SC_VERSION_PROXY, NCBI_TEAMCITY_BUILD_NUMBER_PROXY);
1098 }
1099 #endif
1100 
1101 
SetVersion(const CVersionInfo & version)1102 void CNcbiApplicationAPI::SetVersion(const CVersionInfo& version)
1103 {
1104     if ( s_IsApplicationStarted ) {
1105         ERR_POST_X(19, "SetVersion() should be used from constructor of " \
1106                        "CNcbiApplication derived class, see description");
1107     }
1108     m_Version->SetVersionInfo(new CVersionInfo(version));
1109 }
1110 
SetVersion(const CVersionInfo & version,const SBuildInfo & build_info)1111 void CNcbiApplicationAPI::SetVersion(const CVersionInfo& version,
1112         const SBuildInfo& build_info)
1113 {
1114     if ( s_IsApplicationStarted ) {
1115         ERR_POST_X(19, "SetVersion() should be used from constructor of " \
1116                        "CNcbiApplication derived class, see description");
1117     }
1118     m_Version->SetVersionInfo(new CVersionInfo(version), build_info);
1119 }
1120 
SetFullVersion(CRef<CVersionAPI> version)1121 void CNcbiApplicationAPI::SetFullVersion( CRef<CVersionAPI> version)
1122 {
1123     if ( s_IsApplicationStarted ) {
1124         ERR_POST_X(19, "SetFullVersion() should be used from constructor of "\
1125                        "CNcbiApplication derived class, see description");
1126     }
1127     m_Version.Reset( version );
1128 }
1129 
1130 
GetVersion(void) const1131 CVersionInfo CNcbiApplicationAPI::GetVersion(void) const
1132 {
1133     return m_Version->GetVersionInfo();
1134 }
1135 
GetFullVersion(void) const1136 const CVersionAPI& CNcbiApplicationAPI::GetFullVersion(void) const
1137 {
1138     return *m_Version;
1139 }
1140 
1141 
SetupArgDescriptions(CArgDescriptions * arg_desc)1142 void CNcbiApplicationAPI::SetupArgDescriptions(CArgDescriptions* arg_desc)
1143 {
1144     m_ArgDesc.reset(arg_desc);
1145 
1146     if ( arg_desc ) {
1147         if ( !m_DisableArgDesc ) {
1148             for(CArgDescriptions* desc : m_ArgDesc->GetAllDescriptions()) {
1149             // Add logfile and conffile arguments
1150             if (!desc->Exist(s_ArgLogFile + 1) ) {
1151                 desc->AddOptionalKey
1152                     (s_ArgLogFile+1, "File_Name",
1153                         "File to which the program log should be redirected",
1154                         CArgDescriptions::eOutputFile);
1155             }
1156             if (!desc->Exist(s_ArgCfgFile + 1) ) {
1157                 if (m_DefaultConfig.empty()) {
1158                     desc->AddOptionalKey
1159                         (s_ArgCfgFile + 1, "File_Name",
1160                             "Program's configuration (registry) data file",
1161                             CArgDescriptions::eInputFile);
1162                 } else {
1163                     desc->AddDefaultKey
1164                         (s_ArgCfgFile + 1, "File_Name",
1165                             "Program's configuration (registry) data file",
1166                             CArgDescriptions::eInputFile,
1167                             m_DefaultConfig);
1168                 }
1169             }
1170             }
1171         }
1172         m_Args.reset(arg_desc->CreateArgs(GetArguments()));
1173     } else {
1174         m_Args.reset();
1175     }
1176 }
1177 
1178 
SetupDiag(EAppDiagStream diag)1179 bool CNcbiApplicationAPI::SetupDiag(EAppDiagStream diag)
1180 {
1181     CDiagContext::SetupDiag(diag, 0, eDCM_Flush, m_LogFile);
1182     return true;
1183 }
1184 
1185 
SetupDiag_AppSpecific(void)1186 bool CNcbiApplicationAPI::SetupDiag_AppSpecific(void)
1187 {
1188     CDiagContext::SetupDiag(eDS_ToStderr, 0, eDCM_Flush, m_LogFile);
1189     return true;
1190 }
1191 
1192 
LoadConfig(CNcbiRegistry & reg,const string * conf,CNcbiRegistry::TFlags reg_flags)1193 bool CNcbiApplicationAPI::LoadConfig(CNcbiRegistry&        reg,
1194                                   const string*         conf,
1195                                   CNcbiRegistry::TFlags reg_flags)
1196 {
1197     string basename (m_Arguments->GetProgramBasename(eIgnoreLinks));
1198     string basename2(m_Arguments->GetProgramBasename(eFollowLinks));
1199     CMetaRegistry::SEntry entry;
1200 
1201     if ( !conf ) {
1202         if (reg.IncludeNcbircIfAllowed(reg_flags)) {
1203             m_ConfigPath = CMetaRegistry::FindRegistry
1204                 ("ncbi", CMetaRegistry::eName_RcOrIni);
1205         }
1206         m_ConfigLoaded = true;
1207         return false;
1208     } else if (conf->empty()) {
1209         entry = CMetaRegistry::Load(basename, CMetaRegistry::eName_Ini, 0,
1210                                     reg_flags, &reg);
1211         if ( !entry.registry  &&  basename2 != basename ) {
1212             entry = CMetaRegistry::Load(basename2, CMetaRegistry::eName_Ini, 0,
1213                                         reg_flags, &reg);
1214         }
1215         m_DefaultConfig = CDirEntry(entry.actual_name).GetName();
1216     } else {
1217         entry = CMetaRegistry::Load(*conf, CMetaRegistry::eName_AsIs, 0,
1218                                     reg_flags, &reg);
1219     }
1220     if ( !entry.registry ) {
1221         // failed; complain as appropriate
1222         string dir;
1223         CDirEntry::SplitPath(*conf, &dir, 0, 0);
1224         if (dir.empty()) {
1225             ERR_POST_X(11, Info <<
1226                            "Registry file of application \"" << basename
1227                            << "\" is not found");
1228         } else {
1229             NCBI_THROW(CAppException, eNoRegistry,
1230                        "Registry file \"" + *conf + "\" cannot be opened");
1231         }
1232         // still consider pulling in defaults from .ncbirc
1233         if (reg.IncludeNcbircIfAllowed(reg_flags)) {
1234             m_ConfigPath = CMetaRegistry::FindRegistry
1235                 ("ncbi", CMetaRegistry::eName_RcOrIni);
1236         }
1237         m_ConfigLoaded = true;
1238         return false;
1239     } else if (entry.registry != static_cast<IRWRegistry*>(&reg)) {
1240         // should be impossible with new CMetaRegistry interface...
1241         if (&reg == m_Config  &&  reg.Empty()) {
1242             m_Config.Reset(dynamic_cast<CNcbiRegistry*>
1243                            (entry.registry.GetPointer()));
1244         } else {
1245             // copy into reg
1246             CNcbiStrstream str;
1247             entry.registry->Write(str);
1248             str.seekg(0);
1249             reg.Read(str);
1250         }
1251     }
1252     m_ConfigPath = entry.actual_name;
1253     m_ConfigLoaded = true;
1254     return true;
1255 }
1256 
1257 
LoadConfig(CNcbiRegistry & reg,const string * conf)1258 bool CNcbiApplicationAPI::LoadConfig(CNcbiRegistry& reg,
1259                                   const string*  conf)
1260 {
1261     return LoadConfig(reg, conf, IRegistry::fWithNcbirc);
1262 }
1263 
1264 
1265 CNcbiApplicationAPI::EPreparseArgs
PreparseArgs(int,const char * const *)1266 CNcbiApplicationAPI::PreparseArgs(int                /*argc*/,
1267                                const char* const* /*argv*/)
1268 {
1269     return ePreparse_Continue;
1270 }
1271 
1272 
DisableArgDescriptions(TDisableArgDesc disable)1273 void CNcbiApplicationAPI::DisableArgDescriptions(TDisableArgDesc disable)
1274 {
1275     m_DisableArgDesc = disable;
1276 }
1277 
1278 
HideStdArgs(THideStdArgs hide_mask)1279 void CNcbiApplicationAPI::HideStdArgs(THideStdArgs hide_mask)
1280 {
1281     m_HideArgs = hide_mask;
1282 }
1283 
1284 
SetStdioFlags(TStdioSetupFlags stdio_flags)1285 void CNcbiApplicationAPI::SetStdioFlags(TStdioSetupFlags stdio_flags)
1286 {
1287     // do not call this function more than once
1288     // and from places other than App constructor
1289     _ASSERT(m_StdioFlags == 0);
1290     m_StdioFlags = stdio_flags;
1291 }
1292 
1293 
x_SetupStdio(void)1294 void CNcbiApplicationAPI::x_SetupStdio(void)
1295 {
1296     if ((m_StdioFlags & fNoSyncWithStdio) != 0) {
1297         IOS_BASE::sync_with_stdio(false);
1298     }
1299 
1300     if ((m_StdioFlags & fDefault_CinBufferSize) == 0
1301 #ifdef NCBI_OS_UNIX
1302         &&  !isatty(0)
1303 #endif
1304         ) {
1305 #if defined(NCBI_COMPILER_GCC)  &&  defined(NCBI_OS_SOLARIS)
1306         _ASSERT(!m_CinBuffer);
1307         // Ugly workaround for ugly interaction between g++ and Solaris C RTL
1308         const size_t kCinBufSize = 5120;
1309         m_CinBuffer = new char[kCinBufSize];
1310         cin.rdbuf()->pubsetbuf(m_CinBuffer, kCinBufSize);
1311 #endif
1312     }
1313 #ifdef NCBI_OS_MSWIN
1314     if ((m_StdioFlags & fBinaryCin) != 0) {
1315         NcbiSys_setmode(NcbiSys_fileno(stdin), O_BINARY);
1316     }
1317     if ((m_StdioFlags & fBinaryCout) != 0) {
1318         NcbiSys_setmode(NcbiSys_fileno(stdout), O_BINARY);
1319     }
1320 #endif
1321 }
1322 
x_AddDefaultArgs(void)1323 void CNcbiApplicationAPI::x_AddDefaultArgs(void)
1324 {
1325     if ( !m_DisableArgDesc ) {
1326         for(CArgDescriptions* desc : m_ArgDesc->GetAllDescriptions())
1327         {
1328             if (desc->IsAutoHelpEnabled()) {
1329                 if ((m_HideArgs & fHideHelp) != 0) {
1330                     if (desc->Exist("h")) {
1331                         desc->Delete("h");
1332                     }
1333                 }
1334             }
1335             if ((m_HideArgs & fHideFullHelp) != 0) {
1336                 if (desc->Exist("help")) {
1337                     desc->Delete("help");
1338                 }
1339             }
1340             if ((m_HideArgs & fHideXmlHelp) != 0) {
1341                 if (desc->Exist("xmlhelp")) {
1342                     desc->Delete("xmlhelp");
1343                 }
1344             }
1345             if ((m_HideArgs & fHideLogfile) != 0) {
1346                 if (desc->Exist(s_ArgLogFile + 1)) {
1347                     desc->Delete(s_ArgLogFile + 1);
1348                 }
1349             } else {
1350                 if (!desc->Exist(s_ArgLogFile + 1)) {
1351                     desc->AddOptionalKey
1352                         (s_ArgLogFile+1, "File_Name",
1353                             "File to which the program log should be redirected",
1354                             CArgDescriptions::eOutputFile);
1355                 }
1356             }
1357             if ((m_HideArgs & fHideConffile) != 0) {
1358                 if (desc->Exist(s_ArgCfgFile + 1)) {
1359                     desc->Delete(s_ArgCfgFile + 1);
1360                 }
1361             } else {
1362                 if (!desc->Exist(s_ArgCfgFile + 1)) {
1363                     desc->AddOptionalKey
1364                         (s_ArgCfgFile + 1, "File_Name",
1365                             "Program's configuration (registry) data file",
1366                             CArgDescriptions::eInputFile);
1367                 }
1368             }
1369             if ((m_HideArgs & fHideVersion) != 0) {
1370                 if (desc->Exist(s_ArgVersion + 1)) {
1371                     desc->Delete(s_ArgVersion + 1);
1372                 }
1373             } else {
1374                 if (!desc->Exist(s_ArgVersion + 1)) {
1375                     desc->AddFlag
1376                         (s_ArgVersion + 1,
1377                             "Print version number;  ignore other arguments");
1378                 }
1379             }
1380             if ((m_HideArgs & fHideFullVersion) != 0) {
1381                 if (desc->Exist(s_ArgFullVersion + 1)) {
1382                     desc->Delete(s_ArgFullVersion + 1);
1383                 }
1384                 if (desc->Exist(s_ArgFullVersionXml+ 1)) {
1385                     desc->Delete(s_ArgFullVersionXml + 1);
1386                 }
1387                 if (desc->Exist(s_ArgFullVersionJson + 1)) {
1388                     desc->Delete(s_ArgFullVersionJson + 1);
1389                 }
1390             } else {
1391                 if (!desc->Exist(s_ArgFullVersion + 1)) {
1392                     desc->AddFlag
1393                         (s_ArgFullVersion + 1,
1394                             "Print extended version data;  ignore other arguments");
1395                 }
1396                 if (!desc->Exist(s_ArgFullVersionXml + 1)) {
1397                     desc->AddFlag
1398                         (s_ArgFullVersionXml + 1,
1399                             "Print extended version data in XML format;  ignore other arguments");
1400                 }
1401                 if (!desc->Exist(s_ArgFullVersionJson + 1)) {
1402                     desc->AddFlag
1403                         (s_ArgFullVersionJson + 1,
1404                             "Print extended version data in JSON format;  ignore other arguments");
1405                 }
1406             }
1407             if ((m_HideArgs & fHideDryRun) != 0) {
1408                 if (desc->Exist(s_ArgDryRun + 1)) {
1409                     desc->Delete(s_ArgDryRun + 1);
1410                 }
1411             } else {
1412                 if (!desc->Exist(s_ArgDryRun + 1)) {
1413                     desc->AddFlag
1414                         (s_ArgDryRun + 1,
1415                             "Dry run the application: do nothing, only test all preconditions");
1416                 }
1417             }
1418         }
1419     }
1420 }
1421 
SetProgramDisplayName(const string & app_name)1422 void CNcbiApplicationAPI::SetProgramDisplayName(const string& app_name)
1423 {
1424     if (app_name.empty()) return;
1425     m_ProgramDisplayName = app_name;
1426     // Also set app_name in the diag context
1427     if ( GetDiagContext().GetAppName().empty() ) {
1428         GetDiagContext().SetAppName(app_name);
1429     }
1430 }
1431 
1432 
GetAppName(EAppNameType name_type,int argc,const char * const * argv)1433 string CNcbiApplicationAPI::GetAppName(EAppNameType name_type, int argc,
1434                                     const char* const* argv)
1435 {
1436     CNcbiApplicationGuard instance = InstanceGuard();
1437     string app_name;
1438 
1439     switch (name_type) {
1440     case eBaseName:
1441         if (instance) {
1442             app_name = instance->GetProgramDisplayName();
1443         } else {
1444             string exe_path = FindProgramExecutablePath(argc, argv);
1445             CDirEntry::SplitPath(exe_path, NULL, &app_name);
1446         }
1447         break;
1448 
1449     case eFullName:
1450         if (instance) {
1451             app_name = instance->GetProgramExecutablePath(eIgnoreLinks);
1452         } else {
1453             app_name = FindProgramExecutablePath(argc, argv);
1454         }
1455         break;
1456 
1457     case eRealName:
1458         if (instance) {
1459             app_name = instance->GetProgramExecutablePath(eFollowLinks);
1460         } else {
1461             FindProgramExecutablePath(argc, argv, &app_name);
1462         }
1463         break;
1464     }
1465 
1466     return app_name;
1467 }
1468 
1469 
FindProgramExecutablePath(int argc,const char * const * argv,string * real_path)1470 string CNcbiApplicationAPI::FindProgramExecutablePath
1471 (int                           argc,
1472  const char* const*            argv,
1473  string*                       real_path)
1474 {
1475     CNcbiApplicationGuard instance = InstanceGuard();
1476     string ret_val;
1477     _ASSERT(argc >= 0); // formally signed for historical reasons
1478     _ASSERT(argv != NULL  ||  argc == 0);
1479     if (argc > 0  &&  argv[0] != NULL  &&  argv[0][0] != '\0') {
1480         ret_val = argv[0];
1481     } else if (instance) {
1482         ret_val = instance->GetArguments().GetProgramName();
1483     }
1484 
1485 #if defined(NCBI_OS_MSWIN)  ||  defined(NCBI_OS_UNIX)
1486 
1487 #  ifdef NCBI_OS_MSWIN
1488     // MS Windows: Try more accurate method of detection
1489     // XXX - use this only for real_path?
1490     try {
1491         // Load PSAPI dynamic library -- it should exist on MS-Win NT/2000/XP
1492         CDll dll_psapi("psapi.dll", CDll::eLoadNow, CDll::eAutoUnload);
1493 
1494         // Get function entry-point from DLL
1495         BOOL  (STDMETHODCALLTYPE FAR * dllEnumProcessModules)
1496                 (HANDLE  hProcess,     // handle to process
1497                  HMODULE *lphModule,   // array of module handles
1498                  DWORD   cb,           // size of array
1499                  LPDWORD lpcbNeeded    // number of bytes required
1500                  ) = NULL;
1501 
1502         dllEnumProcessModules =
1503             dll_psapi.GetEntryPoint_Func("EnumProcessModules",
1504                                          &dllEnumProcessModules);
1505         if ( !dllEnumProcessModules ) {
1506             NCBI_THROW(CException, eUnknown, kEmptyStr);
1507         }
1508 
1509         // Find executable file in the midst of all loaded modules
1510         HANDLE  process = GetCurrentProcess();
1511         HMODULE module  = 0;
1512         DWORD   needed  = 0;
1513 
1514         // Get first module of current process (it should be .exe file)
1515         if ( dllEnumProcessModules(process,
1516                                    &module, sizeof(HMODULE), &needed) ) {
1517             if ( needed  &&  module ) {
1518                 TXChar buf[MAX_PATH + 1];
1519                 DWORD ncount = GetModuleFileName(module, buf, MAX_PATH);
1520                 if (ncount > 0) {
1521                     ret_val = _T_STDSTRING(buf);
1522                     if (real_path) {
1523                         *real_path = CDirEntry::NormalizePath(ret_val,
1524                                                               eFollowLinks);
1525                     }
1526                     return ret_val;
1527                 }
1528             }
1529         }
1530     }
1531     catch (const CException&) {
1532         ; // Just catch an all exceptions from CDll
1533     }
1534     // This method didn't work -- use standard method
1535 #  endif
1536 
1537 #  ifdef NCBI_OS_LINUX
1538     // Linux OS: Try more accurate method of detection for real_path
1539     if (ret_val.empty()  &&  !real_path) {
1540         real_path = &ret_val;
1541     }
1542     if (real_path) {
1543         char   buf[PATH_MAX + 1];
1544         string procfile = "/proc/" + NStr::IntToString(getpid()) + "/exe";
1545         int    ncount   = (int)readlink((procfile).c_str(), buf, PATH_MAX);
1546         if (ncount > 0) {
1547             real_path->assign(buf, ncount);
1548             if (real_path == &ret_val  ||  ret_val.empty()) {
1549                 // XXX - could also parse /proc/self/cmdline.
1550                 return *real_path;
1551             }
1552             real_path = 0;
1553         }
1554     }
1555 #  endif
1556 
1557     if (ret_val.empty()) {
1558         // nothing to go on :-/
1559         if (real_path) {
1560             real_path->erase();
1561         }
1562         return kEmptyStr;
1563     }
1564     string app_path = ret_val;
1565 
1566     if ( !CDirEntry::IsAbsolutePath(app_path) ) {
1567 #  ifdef NCBI_OS_MSWIN
1568         // Add default ".exe" extention to the name of executable file
1569         // if it running without extension
1570         string dir, title, ext;
1571         CDirEntry::SplitPath(app_path, &dir, &title, &ext);
1572         if ( ext.empty() ) {
1573             app_path = CDirEntry::MakePath(dir, title, "exe");
1574         }
1575 #  endif
1576         if ( CFile(app_path).Exists() ) {
1577             // Relative path from the the current directory
1578             app_path = CDir::GetCwd() + CDirEntry::GetPathSeparator()+app_path;
1579             if ( !CFile(app_path).Exists() ) {
1580                 app_path = kEmptyStr;
1581             }
1582         } else {
1583             // Running from some path from PATH environment variable.
1584             // Try to determine that path.
1585             string env_path;
1586             if (instance) {
1587                 env_path = instance->GetEnvironment().Get("PATH");
1588             } else {
1589                 env_path = _T_STDSTRING(NcbiSys_getenv(_TX("PATH")));
1590             }
1591             list<string> split_path;
1592 #  ifdef NCBI_OS_MSWIN
1593             NStr::Split(env_path, ";", split_path,
1594                 NStr::fSplit_MergeDelimiters | NStr::fSplit_Truncate);
1595 #  else
1596             NStr::Split(env_path, ":", split_path,
1597                 NStr::fSplit_MergeDelimiters | NStr::fSplit_Truncate);
1598 #  endif
1599             string base_name = CDirEntry(app_path).GetBase();
1600             ITERATE(list<string>, it, split_path) {
1601                 app_path = CDirEntry::MakePath(*it, base_name);
1602                 if ( CFile(app_path).Exists() ) {
1603                     break;
1604                 }
1605                 app_path = kEmptyStr;
1606             }
1607         }
1608     }
1609     ret_val = CDirEntry::NormalizePath
1610         ((app_path.empty() && argv != NULL && argv[0] != NULL) ? argv[0]
1611          : app_path);
1612 
1613 #else  // defined (NCBI_OS_MSWIN)  ||  defined(NCBI_OS_UNIX)
1614 
1615 #  error "Unsupported platform, sorry -- please contact NCBI"
1616 #endif
1617     if (real_path) {
1618         *real_path = CDirEntry::NormalizePath(ret_val, eFollowLinks);
1619     }
1620     return ret_val;
1621 }
1622 
1623 
x_HonorStandardSettings(IRegistry * reg)1624 void CNcbiApplicationAPI::x_HonorStandardSettings( IRegistry* reg)
1625 {
1626     if (reg == 0) {
1627         reg = m_Config.GetPointer();
1628         if (reg == 0)
1629             return;
1630     }
1631 
1632     CStackTrace::s_HonorSignalHandlingConfiguration();
1633 
1634     // [NCBI.MEMORY_FILL]
1635     CObject::SetAllocFillMode(reg->Get("NCBI", "MEMORY_FILL"));
1636 
1637     {{
1638         CSysLog* syslog = dynamic_cast<CSysLog*>(GetDiagHandler());
1639         if (syslog) {
1640             syslog->HonorRegistrySettings(reg);
1641         }
1642     }}
1643 
1644     // Debugging features
1645 
1646     // [DEBUG.DIAG_TRACE]
1647     if ( !reg->Get("DEBUG", DIAG_TRACE).empty() ) {
1648         SetDiagTrace(eDT_Enable, eDT_Enable);
1649     }
1650 
1651     // [DEBUG.ABORT_ON_THROW]
1652     if ( !reg->Get("DEBUG", ABORT_ON_THROW).empty() ) {
1653         SetThrowTraceAbort(true);
1654     }
1655 
1656     // [DEBUG.DIAG_POST_LEVEL]
1657     string post_level = reg->Get("DEBUG", DIAG_POST_LEVEL);
1658     if ( !post_level.empty() ) {
1659         EDiagSev sev;
1660         if (CNcbiDiag::StrToSeverityLevel(post_level.c_str(), sev)) {
1661             SetDiagFixedPostLevel(sev);
1662         }
1663     }
1664 
1665     // [DEBUG.MessageFile]
1666     string msg_file = reg->Get("DEBUG", DIAG_MESSAGE_FILE);
1667     if ( !msg_file.empty() ) {
1668         CDiagErrCodeInfo* info = new CDiagErrCodeInfo();
1669         if ( !info  ||  !info->Read(msg_file) ) {
1670             if ( info ) {
1671                 delete info;
1672             }
1673             ERR_POST_X(12, Warning << "Applications message file \""
1674                            << msg_file
1675                            << "\" is not found");
1676         } else {
1677             SetDiagErrCodeInfo(info);
1678         }
1679     }
1680 
1681     // [DEBUG.GuardAgainstThreadsOnStaticDataDestruction]
1682     if ( !reg->GetBool("DEBUG", "GuardAgainstThreadsOnStaticDataDestruction", true, 0, IRegistry::eErrPost) ) {
1683         CSafeStaticGuard::DisableChildThreadsCheck();
1684     }
1685 
1686     // CPU and memory limitations
1687 
1688     // [NCBI.HeapSizeLimit]
1689     if ( !reg->Get("NCBI", "HeapSizeLimit").empty() ) {
1690         ERR_POST_X(13, Warning
1691                        << "Config param [NCBI.HeapSizeLimit] is deprecated,"
1692                        << "please use [NCBI.MemorySizeLimit] instead.");
1693         int mem_size_limit = reg->GetInt("NCBI", "HeapSizeLimit", 0);
1694         if (mem_size_limit < 0) {
1695             NCBI_THROW(CAppException, eLoadConfig,
1696                        "Configuration file error:  [NCBI.HeapSizeLimit] < 0");
1697         }
1698         SetMemoryLimit(size_t(mem_size_limit) * 1024 * 1024);
1699     }
1700     // [NCBI.MemorySizeLimit]
1701     if ( !reg->Get("NCBI", "MemorySizeLimit").empty() ) {
1702         size_t mem_size_limit = 0;
1703         string s = reg->GetString("NCBI", "MemorySizeLimit", kEmptyStr);
1704         size_t pos = s.find("%");
1705         if (pos != NPOS) {
1706             // Size in percents of total memory
1707             size_t percents = NStr::StringToUInt(CTempString(s, 0, pos));
1708             if (percents > 100) {
1709                 NCBI_THROW(CAppException, eLoadConfig,
1710                            "Configuration file error:  [NCBI.HeapSizeLimit] > 100%");
1711             }
1712             mem_size_limit = (size_t)(CSystemInfo::GetTotalPhysicalMemorySize() * percents / 100);
1713         } else {
1714             try {
1715                 // Size is specified in MiB by default if no suffixes
1716                 // (converted without exception)
1717                 mem_size_limit = NStr::StringToSizet(s) * 1024 * 1024;
1718             }
1719             catch (const CStringException&) {
1720                 // Otherwise, size have suffix (MiB, G, GB, etc)
1721                 Uint8 bytes = NStr::StringToUInt8_DataSize(s);
1722                 if ( bytes > get_limits(mem_size_limit).max() ) {
1723                     NCBI_THROW(CAppException, eLoadConfig,
1724                                "Configuration file error:  [NCBI.MemorySizeLimit] is too big");
1725                 }
1726                 mem_size_limit = (size_t)bytes;
1727             }
1728         }
1729         SetMemoryLimit(mem_size_limit);
1730     }
1731 
1732     // [NCBI.CpuTimeLimit]
1733     if ( !reg->Get("NCBI", "CpuTimeLimit").empty() ) {
1734         int cpu_time_limit = reg->GetInt("NCBI", "CpuTimeLimit", 0);
1735         if (cpu_time_limit < 0) {
1736             NCBI_THROW(CAppException, eLoadConfig,
1737                        "Configuration file error:  [NCBI.CpuTimeLimit] < 0");
1738         }
1739         SetCpuTimeLimit((unsigned int)cpu_time_limit, 5, NULL, NULL);
1740     }
1741 
1742     // TRACE and POST filters
1743 
1744     try {
1745         // [DIAG.TRACE_FILTER]
1746         string trace_filter = reg->Get("DIAG", "TRACE_FILTER");
1747         if ( !trace_filter.empty() )
1748             SetDiagFilter(eDiagFilter_Trace, trace_filter.c_str());
1749     } NCBI_CATCH_X(20,
1750                    "Failed to load and set diag. filter for traces");
1751 
1752     try {
1753         // [DIAG.POST_FILTER]
1754         string post_filter = reg->Get("DIAG", "POST_FILTER");
1755         if ( !post_filter.empty() )
1756             SetDiagFilter(eDiagFilter_Post, post_filter.c_str());
1757     } NCBI_CATCH_X(21,
1758                    "Failed to load and set diag. filter for regular errors");
1759 }
1760 
1761 
AppStart(void)1762 void CNcbiApplicationAPI::AppStart(void)
1763 {
1764     string cmd_line = GetProgramExecutablePath();
1765     if ( m_Arguments.get() ) {
1766         if ( cmd_line.empty() ) {
1767             cmd_line = (*m_Arguments)[0];
1768         }
1769         for (SIZE_TYPE arg = 1; arg < m_Arguments->Size(); ++arg) {
1770             cmd_line += " ";
1771             cmd_line += NStr::ShellEncode((*m_Arguments)[arg]);
1772         }
1773     }
1774 
1775     // Print application start message
1776     if ( !CDiagContext::IsSetOldPostFormat() ) {
1777         GetDiagContext().PrintStart(cmd_line);
1778     }
1779 }
1780 
1781 
AppStop(int exit_code)1782 void CNcbiApplicationAPI::AppStop(int exit_code)
1783 {
1784     CDiagContext& ctx = GetDiagContext();
1785     if ( !ctx.IsSetExitCode() ) {
1786         ctx.SetExitCode(exit_code);
1787     }
1788 }
1789 
1790 
SetExitCode(int exit_code,EExitMode when)1791 void CNcbiApplicationAPI::SetExitCode(int exit_code, EExitMode when)
1792 {
1793     m_ExitCode = exit_code;
1794     m_ExitCodeCond = when;
1795 }
1796 
GetErrCodeString(void) const1797 const char* CAppException::GetErrCodeString(void) const
1798 {
1799     switch (GetErrCode()) {
1800     case eUnsetArgs:  return "eUnsetArgs";
1801     case eSetupDiag:  return "eSetupDiag";
1802     case eLoadConfig: return "eLoadConfig";
1803     case eSecond:     return "eSecond";
1804     case eNoRegistry: return "eNoRegistry";
1805     default:    return CException::GetErrCodeString();
1806     }
1807 }
1808 
1809 
Idle(void)1810 void CDefaultIdler::Idle(void)
1811 {
1812     DiagHandler_Reopen();
1813 }
1814 
1815 
1816 class CIdlerWrapper
1817 {
1818 public:
CIdlerWrapper(void)1819     CIdlerWrapper(void) : m_Idler(new CDefaultIdler()) {}
~CIdlerWrapper(void)1820     ~CIdlerWrapper(void) {}
1821 
1822     INcbiIdler* GetIdler(EOwnership own);
1823     void SetIdler(INcbiIdler* idler, EOwnership own);
1824     void RunIdler(void);
1825 
1826 private:
1827     CMutex              m_Mutex;
1828     AutoPtr<INcbiIdler> m_Idler;
1829 };
1830 
1831 
1832 inline
GetIdler(EOwnership own)1833 INcbiIdler* CIdlerWrapper::GetIdler(EOwnership own)
1834 {
1835     CMutexGuard guard(m_Mutex);
1836     m_Idler.reset(m_Idler.release(), own);
1837     return m_Idler.get();
1838 }
1839 
1840 
1841 inline
SetIdler(INcbiIdler * idler,EOwnership own)1842 void CIdlerWrapper::SetIdler(INcbiIdler* idler, EOwnership own)
1843 {
1844     CMutexGuard guard(m_Mutex);
1845     m_Idler.reset(idler, own);
1846 }
1847 
1848 
1849 inline
RunIdler(void)1850 void CIdlerWrapper::RunIdler(void)
1851 {
1852     if ( m_Idler.get() ) {
1853         CMutexGuard guard(m_Mutex);
1854         if ( m_Idler.get() ) {
1855             m_Idler->Idle();
1856         }
1857     }
1858 }
1859 
1860 
1861 CSafeStatic<CIdlerWrapper> s_IdlerWrapper;
1862 
GetIdler(EOwnership ownership)1863 INcbiIdler* GetIdler(EOwnership ownership)
1864 {
1865     return s_IdlerWrapper.Get().GetIdler(ownership);
1866 }
1867 
1868 
SetIdler(INcbiIdler * idler,EOwnership ownership)1869 void SetIdler(INcbiIdler* idler, EOwnership ownership)
1870 {
1871     s_IdlerWrapper.Get().SetIdler(idler, ownership);
1872 }
1873 
1874 
RunIdler(void)1875 void RunIdler(void)
1876 {
1877     s_IdlerWrapper.Get().RunIdler();
1878 }
1879 
1880 
1881 END_NCBI_SCOPE
1882