libzypp 17.37.17
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>
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
27using std::cout;
28#endif // ZYPP_DBG_VAREXPAND
29
31namespace 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
592namespace zyppintern
593{
594 using namespace zypp;
595 // internal helper called when re-acquiring the lock
597 { repo::RepoVarsMap::instance().clear(); }
598
599} // namespace zyppintern
std::string distributionVersion() const
This is version attribute of the installed base product.
Definition Target.cc:122
Url manipulation class.
Definition Url.h:93
void setViewOptions(const ViewOptions &vopts)
Change the view options of the current object.
Definition Url.cc:921
zypp::url::ViewOptions ViewOptions
View options.
Definition Url.h:103
std::string getUsername(EEncoding eflag=zypp::url::E_DECODED) const
Returns the username from the URL authority.
Definition Url.cc:590
ViewOptions getViewOptions() const
Return the view options of the current object.
Definition Url.cc:914
void setPassword(const std::string &pass, EEncoding eflag=zypp::url::E_DECODED)
Set the password in the URL authority.
Definition Url.cc:757
void setUsername(const std::string &user, EEncoding eflag=zypp::url::E_DECODED)
Set the username in the URL authority.
Definition Url.cc:748
std::string getPassword(EEncoding eflag=zypp::url::E_DECODED) const
Returns the password from the URL authority.
Definition Url.cc:598
Pathname repoManagerRoot() const
The RepoManager root directory.
Definition ZConfig.cc:980
static ZConfig & instance()
Singleton ctor.
Definition ZConfig.cc:940
Pathname varsPath() const
Path containing custom repo variable definitions (configPath()/vars.d).
Definition ZConfig.cc:1168
ZYpp::Ptr getZYpp()
Convenience to get the Pointer to the ZYpp instance.
Definition ZYppFactory.h:77
Namespace intended to collect all environment variables we use.
Definition Env.h:25
std::string ZYPP_REPO_RELEASEVER()
Use faked releasever (e.g.
const StrMatcher & matchNoDots()
Convenience returning StrMatcher( "[^.]*", Match::GLOB )
Definition PathInfo.cc:26
int dirForEach(const Pathname &dir_r, const StrMatcher &matcher_r, function< bool(const Pathname &, const char *const)> fnc_r)
Definition PathInfo.cc:32
std::string asString(const Url &url_r)
Definition Url.cc:948
bool empty() const
Whether neither idents nor provides are set.
void parse(const C_Str &spec_r)
Parse and add spec from a string (IDENT or provides:CAPABILITY`).
bool startsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasPrefix
Definition String.h:1155
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
@ E_ENCODED
Flag to request encoded string(s).
Definition UrlUtils.h:53
Easy-to use interface to the ZYPP dependency resolver.
std::ostream & dumpOn(std::ostream &str, const Capability &obj)
void repoVariablesReset()
Functor expanding repo variables in a string.
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.
std::string operator()(const std::string &value_r, VarRetriever varRetriever_r) const
Return a copy of value_r with embedded variables expanded.
std::string operator()(const std::string &value_r) const
Url operator()(const Url &url_r) const
#define DBG
Definition Logger.h:99
#define MIL
Definition Logger.h:100