1 /* $Id: ncbienv.cpp 624723 2021-02-03 18:51:43Z ivanov $
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: Denis Vakatov, Eugene Vasilchenko
27 *
28 * File Description:
29 * Unified interface to application:
30 * environment -- CNcbiEnvironment
31 * cmd.-line args -- CNcbiArguments
32 *
33 */
34
35 #include <ncbi_pch.hpp>
36 #include <corelib/ncbienv.hpp>
37 #include <corelib/ncbifile.hpp>
38 #include <corelib/ncbi_safe_static.hpp>
39 #include <corelib/error_codes.hpp>
40 #include <common/ncbi_sanitizers.h>
41 #include <algorithm>
42 #include <stdarg.h>
43 #include "ncbisys.hpp"
44
45 #ifdef NCBI_OS_LINUX
46 # include <unistd.h>
47 #endif
48
49 #ifdef NCBI_OS_MSWIN
50 # include <stdlib.h>
51 #elif defined (NCBI_OS_DARWIN)
52 # include <crt_externs.h>
53 # define environ (*_NSGetEnviron())
54 #else
55 extern char** environ;
56 #endif
57
58
59 #define NCBI_USE_ERRCODE_X Corelib_Env
60
61
62 BEGIN_NCBI_SCOPE
63
64
65 ///////////////////////////////////////////////////////
66 // CNcbiEnvironment::
67
68
CNcbiEnvironment(void)69 CNcbiEnvironment::CNcbiEnvironment(void)
70 {
71 Reset(environ);
72 }
73
74
CNcbiEnvironment(const char * const * envp)75 CNcbiEnvironment::CNcbiEnvironment(const char* const* envp)
76 {
77 Reset(envp);
78 }
79
80
~CNcbiEnvironment(void)81 CNcbiEnvironment::~CNcbiEnvironment(void)
82 {
83 return;
84 }
85
86
Reset(const char * const * envp)87 void CNcbiEnvironment::Reset(const char* const* envp)
88 {
89 // load new environment values from "envp"
90 if ( !envp )
91 return;
92
93 CFastMutexGuard LOCK(m_CacheMutex);
94 // delete old environment values
95 m_Cache.clear();
96
97 for ( ; *envp; envp++) {
98 const char* s = *envp;
99 const char* eq = strchr(s, '=');
100 if ( !eq ) {
101 ERR_POST_X(3, "CNcbiEnvironment: bad string '" << s << "'");
102 continue;
103 }
104 m_Cache[string(s, (size_t)(eq - s))] = SEnvValue(eq + 1, kEmptyXCStr);
105 }
106 }
107
108
Get(const string & name,bool * found) const109 const string& CNcbiEnvironment::Get(const string& name, bool* found) const
110 {
111 CFastMutexGuard LOCK(m_CacheMutex);
112 TCache::const_iterator i = m_Cache.find(name);
113 bool fake_found;
114 if (found == NULL) {
115 found = &fake_found;
116 }
117 if ( i != m_Cache.end() ) {
118 if (i->second.ptr == NULL && i->second.value.empty()) {
119 *found = false;
120 return kEmptyStr;
121 } else {
122 *found = true;
123 return i->second.value;
124 }
125 }
126 string loaded_value = Load(name, *found);
127 m_Cache[name] = SEnvValue(loaded_value, *found ? kEmptyXCStr : NULL);
128 const string& s = m_Cache[name].value;
129 return s.empty() ? kEmptyStr : s;
130 }
131
132
Enumerate(list<string> & names,const string & prefix) const133 void CNcbiEnvironment::Enumerate(list<string>& names, const string& prefix)
134 const
135 {
136 names.clear();
137 CFastMutexGuard LOCK(m_CacheMutex);
138 for (TCache::const_iterator it = m_Cache.lower_bound(prefix);
139 it != m_Cache.end() && NStr::StartsWith(it->first, prefix); ++it) {
140 if ( !it->second.value.empty() || it->second.ptr == kEmptyXCStr) {
141 // ignore entries the app cleared out
142 names.push_back(it->first);
143 }
144 }
145 }
146
Set(const string & name,const string & value)147 void CNcbiEnvironment::Set(const string& name, const string& value)
148 {
149
150 TXChar* str = nullptr;
151 {{
152 // Deliberate leak
153 NCBI_LSAN_DISABLE_GUARD;
154 str = NcbiSys_strdup(_T_XCSTRING(name + "=" + value));
155 }}
156 if ( !str ) {
157 throw bad_alloc();
158 }
159
160 if (NcbiSys_putenv(str) != 0) {
161 free(str);
162 NCBI_THROW(CErrnoTemplException<CCoreException>, eErrno,
163 "failed to set environment variable " + name);
164 }
165
166 CFastMutexGuard LOCK(m_CacheMutex);
167 TCache::const_iterator i = m_Cache.find(name);
168 if ( i != m_Cache.end() ) {
169 if (i->second.ptr != NULL && i->second.ptr != kEmptyXCStr) {
170 free(const_cast<TXChar*>(i->second.ptr));
171 }
172 }
173 m_Cache[name] = SEnvValue(value, str);
174 }
175
Unset(const string & name)176 void CNcbiEnvironment::Unset(const string& name)
177 {
178 #ifdef NCBI_OS_MSWIN
179 Set(name, kEmptyStr);
180 #elif defined(NCBI_OS_IRIX)
181 {{
182 char* p = getenv(name.c_str());
183 if (p) {
184 _ASSERT(p[-1] == '=');
185 _ASSERT( !memcmp(p - name.size() - 1, name.data(), name.size()) );
186 p[-1] = '\0';
187 }
188 }}
189 #else
190 unsetenv(name.c_str());
191 #endif
192
193 CFastMutexGuard LOCK(m_CacheMutex);
194 TCache::iterator i = m_Cache.find(name);
195 if ( i != m_Cache.end() ) {
196 if (i->second.ptr != NULL && i->second.ptr != kEmptyXCStr) {
197 free(const_cast<TXChar*>(i->second.ptr));
198 }
199 m_Cache.erase(i);
200 }
201 }
202
Load(const string & name,bool & found) const203 string CNcbiEnvironment::Load(const string& name, bool& found) const
204 {
205 const TXChar* s = NcbiSys_getenv(_T_XCSTRING(name));
206 if (s == NULL) {
207 found = false;
208 return NcbiEmptyString;
209 } else {
210 found = true;
211 return _T_STDSTRING(s);
212 }
213 }
214
215
216
217
218 ///////////////////////////////////////////////////////
219 // CAutoEnvironmentVariable::
220
221
CAutoEnvironmentVariable(const CTempString & var_name,const CTempString & value,CNcbiEnvironment * env)222 CAutoEnvironmentVariable::CAutoEnvironmentVariable(const CTempString& var_name,
223 const CTempString& value,
224 CNcbiEnvironment* env)
225 : m_Env(env, eNoOwnership), m_VariableName(var_name)
226 {
227 if ( !env ) {
228 CNcbiApplicationGuard app = CNcbiApplication::InstanceGuard();
229 if (app) {
230 m_Env.reset(&app->SetEnvironment(), eNoOwnership);
231 } else {
232 m_Env.reset(new CNcbiEnvironment(NULL), eTakeOwnership);
233 }
234 }
235 m_PrevValue = m_Env->Get(m_VariableName, &m_WasSet);
236 if ( value.empty() ) {
237 m_Env->Unset(m_VariableName);
238 } else {
239 m_Env->Set(m_VariableName, value);
240 }
241 }
242
~CAutoEnvironmentVariable()243 CAutoEnvironmentVariable::~CAutoEnvironmentVariable()
244 {
245 if (m_WasSet) {
246 m_Env->Set(m_VariableName, m_PrevValue);
247 } else {
248 m_Env->Unset(m_VariableName);
249 }
250 }
251
252
253
254
255 ///////////////////////////////////////////////////////
256 // CEnvironmentCleaner::
257
258
CEnvironmentCleaner(const char * s,...)259 CEnvironmentCleaner::CEnvironmentCleaner(const char* s, ...)
260 {
261 if (s != NULL) {
262 Clean(s);
263 va_list ap;
264 va_start(ap, s);
265 for (;;) {
266 const char* p = va_arg(ap, const char*);
267 if (p == NULL) {
268 break;
269 }
270 Clean(p);
271 }
272 va_end(ap);
273 }
274 }
275
Clean(const string & name)276 void CEnvironmentCleaner::Clean(const string& name)
277 {
278 CNcbiApplicationGuard app = CNcbiApplication::InstanceGuard();
279 if (app) {
280 app->SetEnvironment().Unset(name);
281 } else {
282 #ifdef NCBI_OS_MSWIN
283 ::SetEnvironmentVariable(_T_XCSTRING(name), NULL);
284 #elif defined(NCBI_OS_IRIX)
285 char* p = getenv(name.c_str());
286 if (p) {
287 _ASSERT(p[-1] == '=');
288 _ASSERT( !memcmp(p - name.size() - 1, name.data(), name.size()) );
289 p[-1] = '\0';
290 }
291 #else
292 unsetenv(name.c_str());
293 #endif
294 }
295 }
296
297
298
299
300 ///////////////////////////////////////////////////////
301 // CNcbiArguments::
302
303
CNcbiArguments(int argc,const char * const * argv,const string & program_name,const string & real_name)304 CNcbiArguments::CNcbiArguments(int argc, const char* const* argv,
305 const string& program_name,
306 const string& real_name)
307 {
308 Reset(argc, argv, program_name, real_name);
309 }
310
311
~CNcbiArguments(void)312 CNcbiArguments::~CNcbiArguments(void)
313 {
314 return;
315 }
316
317
CNcbiArguments(const CNcbiArguments & args)318 CNcbiArguments::CNcbiArguments(const CNcbiArguments& args)
319 : m_ProgramName(args.m_ProgramName),
320 m_Args(args.m_Args),
321 m_ResolvedName(args.m_ResolvedName)
322 {
323 return;
324 }
325
326
operator =(const CNcbiArguments & args)327 CNcbiArguments& CNcbiArguments::operator= (const CNcbiArguments& args)
328 {
329 if (&args == this)
330 return *this;
331
332 m_ProgramName = args.m_ProgramName;
333 m_Args.clear();
334 copy(args.m_Args.begin(), args.m_Args.end(), back_inserter(m_Args));
335 return *this;
336 }
337
338
Reset(int argc,const char * const * argv,const string & program_name,const string & real_name)339 void CNcbiArguments::Reset(int argc, const char* const* argv,
340 const string& program_name, const string& real_name)
341 {
342 // check args
343 if (argc < 0) {
344 NCBI_THROW(CArgumentsException,eNegativeArgc,
345 "Negative number of command-line arguments");
346 }
347
348 if ((argc == 0) != (argv == 0)) {
349 if (argv == 0) {
350 NCBI_THROW(CArgumentsException,eNoArgs,
351 "Command-line arguments are absent");
352 }
353 ERR_POST_X(4, Info <<
354 "CNcbiArguments(): zero \"argc\", non-zero \"argv\"");
355 }
356
357 // clear old args, store new ones
358 m_Args.clear();
359 for (int i = 0; i < argc; i++) {
360 if ( !argv[i] ) {
361 ERR_POST_X(5, Warning <<
362 "CNcbiArguments() -- NULL cmd.-line arg #" << i);
363 continue;
364 }
365 m_Args.push_back(argv[i]);
366 }
367
368 // set application name
369 SetProgramName(program_name, real_name);
370 }
371
372
GetProgramName(EFollowLinks follow_links) const373 const string& CNcbiArguments::GetProgramName(EFollowLinks follow_links) const
374 {
375 if (follow_links) {
376 CFastMutexGuard LOCK(m_ResolvedNameMutex);
377 if ( !m_ResolvedName.size() ) {
378 #ifdef NCBI_OS_LINUX
379 string proc_link = "/proc/" + NStr::IntToString(getpid()) + "/exe";
380 m_ResolvedName = CDirEntry::NormalizePath(proc_link, follow_links);
381 #else
382 m_ResolvedName = CDirEntry::NormalizePath
383 (GetProgramName(eIgnoreLinks), follow_links);
384 #endif
385 }
386 return m_ResolvedName;
387 } else if ( !m_ProgramName.empty() ) {
388 return m_ProgramName;
389 } else if ( m_Args.size() ) {
390 return m_Args[0];
391 } else {
392 static CSafeStatic<string> kDefProgramName;
393 kDefProgramName->assign("ncbi");
394 return kDefProgramName.Get();
395 }
396 }
397
398
GetProgramBasename(EFollowLinks follow_links) const399 string CNcbiArguments::GetProgramBasename(EFollowLinks follow_links) const
400 {
401 const string& name = GetProgramName(follow_links);
402 SIZE_TYPE base_pos = name.find_last_of("/\\:");
403 if (base_pos == NPOS)
404 return name;
405 return name.substr(base_pos + 1);
406 }
407
408
GetProgramDirname(EFollowLinks follow_links) const409 string CNcbiArguments::GetProgramDirname(EFollowLinks follow_links) const
410 {
411 const string& name = GetProgramName(follow_links);
412 SIZE_TYPE base_pos = name.find_last_of("/\\:");
413 if (base_pos == NPOS)
414 return NcbiEmptyString;
415 return name.substr(0, base_pos + 1);
416 }
417
418
SetProgramName(const string & program_name,const string & real_name)419 void CNcbiArguments::SetProgramName(const string& program_name,
420 const string& real_name)
421 {
422 m_ProgramName = program_name;
423 CFastMutexGuard LOCK(m_ResolvedNameMutex);
424 m_ResolvedName = real_name;
425 }
426
427
Add(const string & arg)428 void CNcbiArguments::Add(const string& arg)
429 {
430 m_Args.push_back(arg);
431 }
432
Shift(int n)433 void CNcbiArguments::Shift(int n)
434 {
435 while (n-- > 0) {
436 if (m_Args.size() > 1) {
437 m_Args.erase( ++m_Args.begin());
438 }
439 }
440 }
441
GetErrCodeString(void) const442 const char* CArgumentsException::GetErrCodeString(void) const
443 {
444 switch (GetErrCode()) {
445 case eNegativeArgc: return "eNegativeArgc";
446 case eNoArgs: return "eNoArgs";
447 default: return CException::GetErrCodeString();
448 }
449 }
450
451
452 END_NCBI_SCOPE
453