libzypp  17.37.5
RepoVariables.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
9 #include <iostream>
10 #include <fstream>
11 
12 #include <zypp/base/LogTools.h>
13 #include <zypp/base/String.h>
14 #include <zypp/base/Regex.h>
15 
16 #include <zypp/ZYppFactory.h>
17 #include <zypp/ZConfig.h>
18 #include <zypp/Target.h>
19 #include <zypp/Arch.h>
21 #include <zypp/base/NonCopyable.h>
22 #include <zypp/base/LogTools.h>
23 
24 #define ZYPP_DBG_VAREXPAND 0
25 #if ( ZYPP_DBG_VAREXPAND )
26 #warning ZYPP_DBG_VAREXPAND is on
27 using std::cout;
28 #endif // ZYPP_DBG_VAREXPAND
29 
31 namespace zypp
32 {
33  namespace env
34  {
36  inline std::string ZYPP_REPO_RELEASEVER()
37  {
38  const char * env = getenv("ZYPP_REPO_RELEASEVER");
39  return( env ? env : "" );
40  }
41  }
42 
44  namespace repo
45  {
47  // RepoVarExpand
49  namespace
50  {
55  struct FindVar
56  {
57  bool _embedded;
58  const char * _sbeg;
59  const char * _vbeg;
60  const char * _nbeg;
61  const char * _nend;
62  const char * _vend;
63  const char * _send;
64 
65  FindVar( const std::string & str_r, bool embedded_r )
66  : _embedded( embedded_r )
67  , _sbeg( str_r.c_str() )
68  , _vbeg( nullptr )
69  , _nbeg( nullptr )
70  , _nend( nullptr )
71  , _vend( nullptr )
72  , _send( findVarStart( _sbeg ) )
73  {}
74 
76  bool done() const
77  { return !_send; }
78 
80  bool nextVar()
81  {
82  if ( done() )
83  return false;
84 
85  do {
86  if ( _vbeg && !_vend ) // loop internal: no findVarEnd at current $; skip it
87  _send = findVarStart( _vbeg+1 );
88  _vbeg = _send; // next $ or null if string end
89  _nbeg = _nend = _vend = _send = nullptr;
90  if ( ! _vbeg ) // done!
91  return false;
92  } while( ! findVarEnd() );
93 
94  return true;
95  }
96 
98  bool hasVar() const
99  { return _vend; }
100 
101  //
102  // Methods below are only valid if hasVar() == true
103  //
104 
106  std::string var() const
107  { return std::string( _vbeg, _vend ); }
108 
110  std::string varName() const
111  { return std::string( _nbeg, _nend ); }
112 
114  bool varIsConditional() const
115  { return( *(_vbeg+1) == '{' && *_nend == ':' ); }
116 
123  int varType() const
124  { return( varIsConditional() ? *(_nend+1) : *_vbeg ); }
125 
127  std::string varEmbedded() const
128  { return( varIsConditional() ? std::string( _nend+2, _vend-1 ) : std::string() ); }
129 
130 
132  bool hasVarPrefix() const
133  { return ( _sbeg != _vbeg ); }
134 
136  std::string varPrefix() const
137  { return std::string( _sbeg, _vbeg ); }
138 
140  void wroteVar()
141  { _sbeg = _vend; }
142 
143  private:
145  const char * findVarStart( const char * sbeg_r ) const
146  {
147  for ( ; *sbeg_r; ++sbeg_r )
148  if ( *sbeg_r == '$' || ( _embedded && *sbeg_r == '\\' ) )
149  return sbeg_r;
150  return nullptr;
151  }
152 
154  bool isnamech( int ch ) const
155  { return ch == '_' || isalnum( ch ); }
156 
158  bool findVarEnd()
159  {
160  // asserted: *_vbeg == '$' || '\\'
161  if ( ! findVarEnd( _vbeg, _nbeg, _nend, _vend ) )
162  return false;
163  _send = findVarStart( _vend );
164  return true;
165  }
166 
168  const char * findVarEnd( const char * vbeg ) const
169  {
170  // asserted: *_vbeg == '$'
171  const char * nbeg = nullptr;
172  const char * nend = nullptr;
173  const char * vend = nullptr;
174  findVarEnd( vbeg, nbeg, nend, vend );
175  return vend;
176  }
177 
179  bool findVarEnd( const char * vbeg, const char *& nbeg, const char *& nend, const char *& vend ) const
180  {
181  // embedded only: handle backslash escaped chars
182  if ( *_vbeg == '\\' )
183  {
184  nbeg = vbeg+1;
185  if ( *nbeg == '$'
186  || *nbeg == '}'
187  || *nbeg == '\\' )
188  {
189  nend = vend = vbeg+2;
190  return true;
191  }
192  return false;
193  }
194 
195  // asserted: *vbeg == '$'
196  // vbeg: [$]{variable:-word} / [$]{variable}
197  // nbeg: ${[v]ariable:-word} / ${[v]ariable}
198  bool braced = ( *(vbeg+1) == '{' ); //}
199  nbeg = vbeg+( braced ? 2 : 1 );
200  if ( !isnamech( *nbeg ) ) // don't allow empty var name
201  return false;
202  for ( nend = nbeg+1; isnamech( *nend ); ++nend )
203  {;} // skip over var name
204  // nend: ${variable[:]-word} / ${variable[}]
205 
206  // vend: ${variable:-word}[] / ${variable}[]
207  // stay with ( vend == nullptr ) until you know it's valid
208  if ( braced )
209  {
210  if ( *nend == '}' )
211  {
212  vend = nend+1;
213  }
214  else if ( *nend == ':' )
215  {
216  const char * scan = nend+1;
217  if ( *scan == '+' || *scan == '-' )
218  {
219  ++scan;
220  // find first not escaped '}'
221  while ( *scan )
222  {
223  if ( *scan == '\\' )
224  {
225  ++scan; // next char is skipped
226  if ( *scan )
227  ++scan;
228  }
229  else if ( *scan == '$' )
230  {
231  // an embedded var?
232  if ( ! (scan = findVarEnd( scan )) )
233  return false;
234  }
235  else if ( *scan == '}' )
236  {
237  vend = scan+1; // ==> unesacped '}', we're done!
238  break;
239  }
240  else
241  ++scan; // literal
242  }
243  // ( ! *scan ) => end of string while looking for unesacped '}'
244  }
245  else
246  ; // err: ':' not followed by '+' or '-'
247  }
248  else
249  ; // err: braced name must end with '}' or ':'
250  }
251  else
252  {
253  vend = nend; // un-braced
254  }
255  return( vend != nullptr );
256  }
257  };
258 
259  bool _expand( std::string &, const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r );
260 
261  inline std::string expand( const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
262  {
263  std::string ret;
264  if ( ! _expand( ret, value_r, level_r, varRetriever_r ) )
265  ret = value_r;
266  return ret;
267  }
268 
269  inline std::string expand( std::string && value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
270  {
271  std::string ret;
272  if ( ! _expand( ret, value_r, level_r, varRetriever_r ) )
273  ret = std::move(value_r);
274  return ret;
275  }
276 
280  inline bool _expand( std::string & result_r, const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
281  {
282 #if ( ZYPP_DBG_VAREXPAND )
283  cout << std::string( 2*level_r, ' ' ) << "\033[7m>>" << value_r << "<<\033[27m" << endl;
284  std::ostringstream dbg;
285  const char * dbgsbeg = value_r.c_str(); // track vars we already added to dbg
286  unsigned dbgi = 0; // color 1-5 var / 6 moved value_r
287  dbg << std::string( 2*level_r, ' ' ) << ">>";
288 #endif // ZYPP_DBG_VAREXPAND
289 
290  bool expanded = false;
291 
292  if ( ! value_r.empty() )
293  {
294  FindVar scan( value_r, level_r ); // level_r > 0 is embedded
295  while ( scan.nextVar() )
296  {
297  static const std::string _emptyValue;
298  const std::string *const knownVar = ( varRetriever_r ? varRetriever_r( scan.varName() ) : nullptr );
299  const std::string & varValue( knownVar ? *knownVar : _emptyValue );
300 
301 #if ( ZYPP_DBG_VAREXPAND )
302  dbg << std::string(dbgsbeg,scan._vbeg) << "\033[3" << ((dbgi%5)+1) << "m" << scan.var() << "\033[0m";
303  cout << dbg.str() << "|<< " << scan.varName() << " " << (knownVar?"("+varValue+")":"-") << " {" << scan.varEmbedded() << "}" << endl;
304  dbgsbeg = scan._vend;
305  dbgi++;
306 #endif // ZYPP_DBG_VAREXPAND
307 
308  bool mustSubstitute = false; // keep original text per default
309  std::string substitutionValue;
310 
311  int varType = scan.varType();
312  if ( varType == '$' ) // plain var
313  {
314  if ( knownVar )
315  {
316  mustSubstitute = true;
317  substitutionValue = varValue;
318  }
319  else
320  ; // keep original text per default
321  }
322  else if ( varType == '-' ) // ':-' default value
323  {
324  mustSubstitute = true;
325  if ( varValue.empty() )
326  substitutionValue = expand( scan.varEmbedded(), level_r+1, varRetriever_r );
327  else
328  substitutionValue = varValue;
329  }
330  else if ( varType == '+' ) // ':+' alternate value
331  {
332  mustSubstitute = true;
333  if ( ! varValue.empty() )
334  substitutionValue = expand( scan.varEmbedded(), level_r+1, varRetriever_r );
335  else
336  ; // empty substitutionValue
337  }
338  else if ( varType == '\\' ) // backslash escaped literal (in varName)
339  {
340  mustSubstitute = true;
341  substitutionValue = scan.varName();
342  }
343  else
344  ; // keep original text per default
345 
346  if ( mustSubstitute )
347  {
348  if ( scan.hasVarPrefix() )
349  result_r += scan.varPrefix();
350  if ( ! substitutionValue.empty() )
351  result_r += substitutionValue;
352  scan.wroteVar(); // this moves scan._sbeg so we can later see what's already written
353  }
354  }
355 
356 #if ( ZYPP_DBG_VAREXPAND )
357  dbg << std::string( dbgsbeg ) << (scan._sbeg == value_r.c_str() ? "<<\033[36m(moved)\033[0m" : "");
358 #endif // ZYPP_DBG_VAREXPAND
359 
360  // handle unwritten data:
361  if ( scan._sbeg != value_r.c_str() )
362  {
363  expanded = true;
364  if ( *scan._sbeg )
365  result_r += std::string( scan._sbeg );
366  }
367  else
368  ; // no replacements at all
369  }
370 
371 #if ( ZYPP_DBG_VAREXPAND )
372  dbg << "<<";
373  cout << dbg.str() << endl;
374  cout << std::string( 2*level_r, ' ' ) << "\033[36m->" << result_r << "<-\033[0m" << endl;
375 #endif // ZYPP_DBG_VAREXPAND
376  return expanded;
377  }
378  } // namespace
380 
381  std::string RepoVarExpand::operator()( const std::string & value_r, VarRetriever varRetriever_r ) const
382  { return expand( value_r, 0, varRetriever_r ); }
383 
384  std::string RepoVarExpand::operator()( std::string && value_r, VarRetriever varRetriever_r ) const
385  { return expand( std::move(value_r), 0, varRetriever_r ); }
386 
388  // RepoVariables*Replace
390  namespace
391  {
392  class RepoVarsMap : public std::map<std::string,std::string>
393  {
394  public:
395  static RepoVarsMap & instance()
396  { static RepoVarsMap _instance; return _instance; }
397 
398  static const std::string * lookup( const std::string & name_r )
399  { return instance()._lookup( name_r ); }
400 
401  private:
402  const std::string * _lookup( const std::string & name_r )
403  {
404  // Safe guard in case the caller does not own a zypp instance. In this case
405  // getZYpp()->getTarget() in checkOverride would create a zypp instance which
406  // would clear the variables parsed so far.
407  auto guard { getZYpp() };
408 
409  // bsc#1237044: The context in which the variables are to be evaluated.
410  // TODO: In fact we should have a RepoVarsMap per context, no singleton.
411  // The code here reflects the weakness of the classic ZConfig singleton.
412  // We need to reset if the contextRoot changes and can't have different
413  // ZConfig::instance().varsPath() per context.
414  const Pathname contextRoot { ZConfig::instance().repoManagerRoot() };
415  const Pathname contextVarsPath { ZConfig::instance().varsPath() };
416  if ( contextRoot != _contextRoot ) {
417  MIL << "RepoVars context changed from " << _contextRoot << " -> " << contextRoot << endl;
418  _contextRoot = contextRoot;
419  clear();
420  }
421 
422  if ( empty() ) // at init / after reset
423  {
424  // load user definitions from vars.d
425  filesystem::dirForEach( contextRoot / contextVarsPath,
426  filesystem::matchNoDots(), bind( &RepoVarsMap::parse, this, _1, _2 ) );
427  // releasever_major/_minor are per default derived from releasever.
428  // If releasever is userdefined, inject missing _major/_minor too.
429  deriveFromReleasever( "releasever", /*dont't overwrite user defined values*/false );
430 
431  dumpOn( DBG );
432  // add builtin vars except for releasever{,_major,_minor} (see checkOverride)
433  {
434  const Arch & arch( ZConfig::instance().systemArchitecture() );
435  {
436  std::string & var( operator[]( "arch" ) );
437  if ( var.empty() ) var = arch.asString();
438  }
439  {
440  std::string & var( operator[]( "basearch" ) );
441  if ( var.empty() ) var = arch.baseArch().asString();
442  }
443  }
444  }
445 
446  const std::string * ret = checkOverride( name_r, contextRoot );
447  if ( !ret )
448  {
449  // get value from map
450  iterator it = find( name_r );
451  if ( it != end() )
452  ret = &(it->second);
453  }
454 
455  return ret;
456  }
457 
458  std::ostream & dumpOn( std::ostream & str ) const
459  {
460  for ( auto && kv : *this )
461  {
462  str << '{' << kv.first << '=' << kv.second << '}' << endl;
463  }
464  return str;
465  }
466 
467  private:
469  bool parse( const Pathname & dir_r, const std::string & str_r )
470  {
471  std::ifstream file( (dir_r/str_r).c_str() );
472  operator[]( str_r ) = str::getline( file, /*trim*/false );
473  return true;
474  }
475 
477  void deriveFromReleasever( const std::string & stem_r, bool overwrite_r )
478  {
479  if ( count( stem_r ) ) // releasever is defined..
480  {
481  const std::string & stem_major( stem_r+"_major" );
482  const std::string & stem_minor( stem_r+"_minor" );
483  if ( overwrite_r )
484  splitReleaseverTo( operator[]( stem_r ), &operator[]( stem_major ), &operator[]( stem_minor ) );
485  else
486  splitReleaseverTo( operator[]( stem_r ),
487  count( stem_major ) ? nullptr : &operator[]( stem_major ),
488  count( stem_minor ) ? nullptr : &operator[]( stem_minor ) );
489  }
490  }
491 
493  void splitReleaseverTo( const std::string & releasever_r, std::string * major_r, std::string * minor_r ) const
494  {
495  if ( major_r || minor_r )
496  {
497  std::string::size_type pos = releasever_r.find( '.' );
498  if ( pos == std::string::npos )
499  {
500  if ( major_r ) *major_r = releasever_r;
501  if ( minor_r ) minor_r->clear();
502  }
503  else
504  {
505  if ( major_r ) *major_r = releasever_r.substr( 0, pos );
506  if ( minor_r ) *minor_r = releasever_r.substr( pos+1 ) ;
507  }
508  }
509  }
510 
512  const std::string * checkOverride( const std::string & name_r, const Pathname & contextRoot_r )
513  {
515  // Always check for changing releasever{,_major,_minor} (bnc#943563)
516  if ( str::startsWith( name_r, "releasever" )
517  && ( name_r.size() == 10
518  || strcmp( name_r.c_str()+10, "_minor" ) == 0
519  || strcmp( name_r.c_str()+10, "_major" ) == 0 ) )
520  {
521  std::string val( env::ZYPP_REPO_RELEASEVER() );
522  if ( !val.empty() )
523  {
524  // $ZYPP_REPO_RELEASEVER always overwrites any defined value
525  if ( val != operator[]( "$releasever" ) )
526  {
527  operator[]( "$releasever" ) = std::move(val);
528  deriveFromReleasever( "$releasever", /*overwrite previous values*/true );
529  }
530  return &operator[]( "$"+name_r );
531  }
532  else if ( !count( name_r ) )
533  {
534  // No user defined value, so we follow the target
535  val = Target::distributionVersion( contextRoot_r );
536 
537  if ( val != operator[]( "$_releasever" ) )
538  {
539  operator[]( "$_releasever" ) = std::move(val);
540  deriveFromReleasever( "$_releasever", /*overwrite previous values*/true );
541  }
542  return &operator[]( "$_"+name_r );
543  }
544  // else:
545  return nullptr; // get user value from map
546  }
548 
549  return nullptr; // get user value from map
550  }
551 
552  private:
553  Pathname _contextRoot;
554  };
555  } // namespace
557 
558  std::string RepoVariablesStringReplacer::operator()( const std::string & value ) const
559  {
560  return RepoVarExpand()( value, RepoVarsMap::lookup );
561  }
562  std::string RepoVariablesStringReplacer::operator()( std::string && value ) const
563  {
564  return RepoVarExpand()( std::move(value), RepoVarsMap::lookup );
565  }
566 
568  {
569  Url::ViewOptions toReplace = value.getViewOptions() - url::ViewOption::WITH_USERNAME - url::ViewOption::WITH_PASSWORD;
570  // Legacy: Not 100% correct because it substitutes inside the 'proxypass=' value,
571  // but this was done before as well. The final fix will have to keep the proxypasswd
572  // out side the url in a cedential file.
573  Url tmpurl { value };
574  tmpurl.setViewOptions( toReplace );
575  const std::string & replaced( RepoVarExpand()( hotfix1050625::asString( tmpurl ), RepoVarsMap::lookup ) );
576 
577  Url newurl;
578  if ( !replaced.empty() )
579  {
580  newurl = replaced;
583  newurl.setViewOptions( value.getViewOptions() );
584  }
585  return newurl;
586  }
587  } // namespace repo
589 } // namespace zypp
592 namespace zyppintern
593 {
594  using namespace zypp;
595  // internal helper called when re-acquiring the lock
597  { repo::RepoVarsMap::instance().clear(); }
598 
599 } // namespace zyppintern
void setPassword(const std::string &pass, EEncoding eflag=zypp::url::E_DECODED)
Set the password in the URL authority.
Definition: Url.cc:757
#define MIL
Definition: Logger.h:100
const char * _nend
${variable[:]-word} / ${variable[}]
const char * _vend
${variable:-word}[] / ${variable}[]
std::string getline(std::istream &str, const Trim trim_r)
Return stream content up to (but not returning) the next newline.
Definition: String.cc:481
Namespace intended to collect all environment variables we use.
Definition: Env.h:24
std::string distributionVersion() const
This is version attribute of the installed base product.
Definition: Target.cc:122
function< const std::string *(const std::string &)> VarRetriever
Function taking a variable name and returning a pointer to the variable value or nullptr if unset...
Definition: RepoVariables.h:60
static ZConfig & instance()
Singleton ctor.
Definition: ZConfig.cc:940
Flag to request encoded string(s).
Definition: UrlUtils.h:53
void setViewOptions(const ViewOptions &vopts)
Change the view options of the current object.
Definition: Url.cc:921
Pathname varsPath() const
Path containing custom repo variable definitions (configPath()/vars.d).
Definition: ZConfig.cc:1168
const char * _vbeg
[$]{variable:-word} / [$]{variable} / if embedded also on [\]
int dirForEach(const Pathname &dir_r, const StrMatcher &matcher_r, function< bool(const Pathname &, const char *const)> fnc_r)
Definition: PathInfo.cc:32
String related utilities and Regular expression matching.
void setUsername(const std::string &user, EEncoding eflag=zypp::url::E_DECODED)
Set the username in the URL authority.
Definition: Url.cc:748
Url::asString() view options.
Definition: UrlBase.h:40
bool _embedded
A (formerly) embedded string may have esacped $, closebrace and backslash.
ViewOptions getViewOptions() const
Return the view options of the current object.
Definition: Url.cc:914
std::string operator()(const std::string &value_r) const
Pathname repoManagerRoot() const
The RepoManager root directory.
Definition: ZConfig.cc:980
const StrMatcher & matchNoDots()
Convenience returning StrMatcher( "[^.]*", Match::GLOB )
Definition: PathInfo.cc:26
Functor expanding repo variables in a string.
Definition: RepoVariables.h:57
bool startsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasPrefix
Definition: String.h:1156
std::string asString(const Url &url_r)
Definition: Url.cc:948
std::ostream & dumpOn(std::ostream &str, const Capability &obj)
Definition: Capability.cc:580
Pathname _contextRoot
const char * _send
end of scan (next $ or nullptr if done)
const char * _sbeg
start of string to scan
Url operator()(const Url &url_r) const
std::string operator()(const std::string &value_r, VarRetriever varRetriever_r) const
Return a copy of value_r with embedded variables expanded.
Easy-to use interface to the ZYPP dependency resolver.
Definition: Application.cc:19
SolvableIdType size_type
Definition: PoolMember.h:126
const char * _nbeg
${[v]ariable:-word} / ${[v]ariable}
std::string getPassword(EEncoding eflag=zypp::url::E_DECODED) const
Returns the password from the URL authority.
Definition: Url.cc:598
std::string ZYPP_REPO_RELEASEVER()
Use faked releasever (e.g.
Url manipulation class.
Definition: Url.h:92
#define DBG
Definition: Logger.h:99
void repoVariablesReset()
std::string getUsername(EEncoding eflag=zypp::url::E_DECODED) const
Returns the username from the URL authority.
Definition: Url.cc:590