Pol  Revision:4b29d2b
pkg.cpp
Go to the documentation of this file.
1 
7 #include "pkg.h"
8 
9 #include <stdlib.h>
10 #include "pol_global_config.h"
11 
12 #include "../clib/cfgelem.h"
13 #include "../clib/cfgfile.h"
14 #include "../clib/clib.h"
15 #include "../clib/dirlist.h"
16 #include "../clib/fileutil.h"
17 #include "../clib/logfacility.h"
18 #include "../clib/passert.h"
19 #include "../clib/stlutil.h"
20 #include "../clib/strutil.h"
21 #include "../plib/systemstate.h"
22 
23 namespace Pol
24 {
25 namespace Plib
26 {
28 {
30 }
31 
32 
33 Package* find_package( const std::string& pkgname )
34 {
35  auto itr = systemstate.packages_byname.find( pkgname );
36  if ( itr != systemstate.packages_byname.end() )
37  {
38  return ( *itr ).second;
39  }
40  else
41  {
42  return NULL;
43  }
44 }
45 
46 void remove_package( Package* pkg )
47 {
48  auto last = std::remove_if( systemstate.packages.begin(), systemstate.packages.end(),
49  std::bind2nd( std::equal_to<Package*>(), pkg ) );
50  systemstate.packages.erase( last, systemstate.packages.end() );
51 
52  // TODO: Check this loop. It looks odd.
53  // Should the loop stop after removing the package?
54  // Is it possible to have more than one name for the same package?
55 
56  auto itr = systemstate.packages_byname.begin();
57  while ( itr != systemstate.packages_byname.end() )
58  {
59  auto tempitr = itr;
60  ++itr;
61  if ( ( *tempitr ).second == pkg )
62  systemstate.packages_byname.erase( tempitr );
63  }
64 }
65 
66 void compare_versions( const std::string& verleft, const std::string& verright, bool& isgreater,
67  bool& isequal )
68 {
69  isgreater = isequal = false;
70 
71  const char* vneed = verright.c_str();
72  const char* vhave = verleft.c_str();
73 
74  while ( ( vneed != NULL && vneed[0] != '\0' ) || ( vhave != NULL && vhave[0] != '\0' ) )
75  {
76  char* new_vneed = NULL;
77  char* new_vhave = NULL;
78  unsigned int vneedpart, vhavepart;
79  vneedpart = ( vneed != NULL ) ? strtoul( vneed, &new_vneed, 0 ) : 0;
80  vhavepart = ( vhave != NULL ) ? strtoul( vhave, &new_vhave, 0 ) : 0;
81 
82  if ( vhavepart > vneedpart )
83  {
84  isgreater = true;
85  return;
86  }
87  else if ( vhavepart < vneedpart )
88  {
89  return;
90  }
91  else // same, so check the next one
92  {
93  vneed = new_vneed;
94  if ( vneed && *vneed )
95  ++vneed;
96  vhave = new_vhave;
97  if ( vhave && *vhave )
98  ++vhave;
99  }
100  }
101  isequal = true;
102 }
103 
104 bool check_version2( const std::string& version_have, const std::string& version_need )
105 {
106  bool isgreater;
107  bool isequal;
108  compare_versions( version_have, version_need, isgreater, isequal );
109  return ( isequal || isgreater );
110 }
111 
113 { // have // need
114  passert( check_version2( "0", "0" ) == true );
115  passert( check_version2( "1", "0" ) == true );
116  passert( check_version2( "0", "1" ) == false );
117  passert( check_version2( "0.5", "1" ) == false );
118  passert( check_version2( "0.5", "0" ) == true );
119  passert( check_version2( "1.2", "1.12" ) == false );
120  passert( check_version2( "1.12", "1.2" ) == true );
121  passert( check_version2( "1.2.3", "1" ) == true );
122  passert( check_version2( "1.1", "1.2.3" ) == false );
123  passert( check_version2( "1.3", "1.2.3" ) == true );
124 }
125 
127 {
128  std::string tmp;
129  while ( elem.remove_prop( tag, &tmp ) )
130  {
131  Clib::mklower( tmp );
132  ISTRINGSTREAM is( tmp );
133  Elem _elem;
134  if ( is >> _elem.pkgname )
135  {
136  if ( !( is >> _elem.version ) )
137  _elem.version = "0";
138 
139  elems.push_back( _elem );
140  }
141  }
142 }
144 {
145  size_t size = sizeof( PackageList );
146  for ( const auto& elem : elems )
147  size += elem.pkgname.capacity() + elem.version.capacity();
148  return size;
149 }
150 
151 Package::Package( const std::string& pkg_dir, Clib::ConfigElem& elem )
152  : dir_( pkg_dir ),
153  name_( elem.remove_string( "Name" ) ),
154  version_( elem.remove_string( "Version", "0" ) ),
155  core_required_( 0 ),
156  requires_( elem, "Requires" ),
157  conflicts_( elem, "Conflicts" ),
158  replaces_( elem, "Replaces" ),
159  provides_system_home_page_( elem.remove_bool( "ProvidesSystemHomePage", false ) )
160 {
161  Clib::mklower( name_ );
162  // CoreRequired can either be a number (94,,95 etc)
163  // or a version string (POL095-2003-01-28)
164  std::string tmp = elem.read_string( "CoreRequired", "0" );
165  if ( isdigit( tmp[0] ) )
166  {
167  // the first kind - a number.
168  core_required_ = elem.remove_ushort( "CoreRequired", 0 );
169  }
170  else
171  {
172  core_versionstring_required_ = elem.remove_string( "CoreRequired" );
173  }
174 }
175 
176 std::string Package::desc() const
177 {
178  return name() + " (" + dir() + ")";
179 }
180 
182 {
183  bool any = false;
184  for ( const auto& elem : replaces_.elems )
185  {
186  Package* found = find_package( elem.pkgname );
187  if ( found != NULL )
188  {
189  any = true;
190  INFO_PRINT << "Package " << desc() << " replaces package " << found->desc() << "\n";
191  remove_package( found );
192  delete found;
193  found = NULL;
194  }
195  }
196  return any;
197 }
198 
200 {
201  if ( core_required_ )
202  {
203  if ( core_required_ > POL_VERSION ) // TODO: use a more fine grained check here
204  {
205  ERROR_PRINT << "Error in package " << desc() << ":\n"
206  << " Core version " << core_required_ << " is required, but version "
207  << POL_VERSION << " is running.\n";
208  throw std::runtime_error( "Package requires a newer core version" );
209  }
210  }
211  else if ( !core_versionstring_required_.empty() )
212  {
213  int cmp = stricmp( POL_VERSION_ID, core_versionstring_required_.c_str() );
214  if ( cmp < 0 )
215  {
216  ERROR_PRINT << "Error in package " << desc() << ":\n"
217  << " Core version " << core_versionstring_required_
218  << " is required, but version " << POL_VERSION_ID << " is running.\n";
219  throw std::runtime_error( "Package requires a newer core version" );
220  }
221  }
222  for ( const auto& elem : requires_.elems )
223  {
224  Package* found = find_package( elem.pkgname );
225  if ( found == NULL )
226  {
227  ERROR_PRINT << "Error in package '" << name_ << "' (" << dir_ << "):\n"
228  << " Package '" << elem.pkgname << "' is required, but is not installed.\n";
229  throw std::runtime_error( "Package dependency error" );
230  }
231  else
232  {
233  if ( !check_version2( found->version_, elem.version ) )
234  {
235  ERROR_PRINT << "Error in package '" << name_ << "' (" << dir_ << "):\n"
236  << " Package '" << elem.pkgname << "' version " << elem.version
237  << " is required, but version " << found->version_ << " was found\n";
238  throw std::runtime_error( "Package dependency error" );
239  }
240  }
241  }
242 }
243 
245 {
246  for ( const auto& elem : conflicts_.elems )
247  {
248  Package* found = find_package( elem.pkgname );
249  if ( found != NULL )
250  {
251  ERROR_PRINT << "Error in package " << desc() << ":\n"
252  << " Package conflicts with package " << found->desc() << "\n";
253  throw std::runtime_error( "Package dependency error" );
254  }
255  }
256 }
257 
258 size_t Package::estimateSize() const
259 {
260  size_t size = dir_.capacity() + name_.capacity() + version_.capacity() +
261  sizeof( unsigned short ) /*core_required*/
264  sizeof( bool ) /*provides_system_home_page_*/
265  ;
266  return size;
267 }
268 
269 void load_package( const std::string& pkg_dir, Clib::ConfigElem& elem, bool quiet )
270 {
271  std::unique_ptr<Package> pkg( new Package( pkg_dir, elem ) );
272  Package* existing_pkg = find_package( pkg->name() );
273 
274  if ( existing_pkg != NULL )
275  {
276  bool isgreater, isequal;
277  compare_versions( pkg->version(), existing_pkg->version(), isgreater, isequal );
278  if ( isgreater )
279  {
280  // replace existing package with newer version
281  if ( !quiet )
282  INFO_PRINT << "Replacing package " << existing_pkg->desc() << " version "
283  << existing_pkg->version() << " with version " << pkg->version() << " found in "
284  << pkg->dir() << "\n";
285  remove_package( existing_pkg );
286  delete existing_pkg;
287  existing_pkg = NULL;
288  }
289  else if ( isequal )
290  {
291  ERROR_PRINT << "Error in package " << pkg->desc() << ":\n"
292  << " Package by same name already found in " << existing_pkg->desc() << "\n";
293  throw std::runtime_error( "Duplicate package found" );
294  }
295  else
296  {
297  // skip this package, its version is older
298  if ( !quiet )
299  INFO_PRINT << "Skipping package " << pkg->desc() << " version " << pkg->version()
300  << " because version " << existing_pkg->version() << " was already found in "
301  << existing_pkg->dir() << "\n";
302  return;
303  }
304  }
305 
306  systemstate.packages.push_back( pkg.get() );
307  Package* ppkg = pkg.release();
308  systemstate.packages_byname.insert( PackagesByName::value_type( ppkg->name(), ppkg ) );
309 }
310 
311 
312 void load_packages( const std::string& basedir, bool quiet )
313 {
314  for ( Clib::DirList dl( basedir.c_str() ); !dl.at_end(); dl.next() )
315  {
316  std::string dirname = dl.name();
317  if ( dirname[0] == '.' )
318  continue;
319  if ( dirname == "template" )
320  continue;
321 
322  std::string pkg_dir = basedir + dirname + "/";
323  std::string pkg_cfg = pkg_dir + "pkg.cfg";
324 
325  if ( Clib::FileExists( pkg_cfg.c_str() ) )
326  {
327  Clib::ConfigFile cf( pkg_cfg.c_str() );
328  Clib::ConfigElem elem;
329 
330  cf.readraw( elem );
331  std::string enabled_pkg = pkg_dir + "enabled.pkg";
332  std::string disabled_pkg = pkg_dir + "disabled.pkg";
333 
334  if ( ( elem.remove_bool( "Enabled" ) == true || Clib::FileExists( enabled_pkg.c_str() ) ) &&
335  !Clib::FileExists( disabled_pkg.c_str() ) )
336  {
337  if ( !quiet )
338  INFO_PRINT << "Loading package in " << pkg_dir << "\n";
339  load_package( pkg_dir, elem, quiet );
340 
341  load_packages( pkg_dir, quiet );
342  }
343  }
344  else
345  {
346  load_packages( pkg_dir, quiet );
347  }
348  }
349 }
350 
351 void check_deps_for_package( const Package* pkg )
352 {
353  pkg->check_dependencies();
354  pkg->check_conflicts();
355 }
356 
358 {
359  bool done;
360  do
361  {
362  done = true;
363 
364  for ( const auto& pkg : systemstate.packages )
365  {
366  bool change = pkg->check_replacements();
367  if ( change )
368  {
369  done = false;
370  break;
371  }
372  }
373 
374  } while ( !done );
375 }
376 
378 {
379  for ( const auto& pkg : systemstate.packages )
380  check_deps_for_package( pkg );
381 }
382 
383 void load_packages( bool quiet )
384 {
386 
387  load_packages( "pkg/", quiet );
388 
389  if ( Clib::FileExists( "config/pkgroots.cfg" ) )
390  {
391  Clib::ConfigFile cf( "config/pkgroots.cfg", "PackageRoot" );
392  Clib::ConfigElem elem;
393  while ( cf.read( elem ) )
394  {
395  std::string dir;
396  while ( elem.remove_prop( "dir", &dir ) )
397  {
398  dir = Clib::normalized_dir_form( dir );
399  INFO_PRINT << "Searching for packages under " << dir << "\n";
400  load_packages( dir.c_str(), quiet );
401  }
402  }
403  }
404 
406 
408 }
409 
410 bool pkgdef_split( const std::string& spec, const Package* inpkg, const Package** outpkg,
411  std::string* path )
412 {
413  if ( spec[0] == ':' )
414  {
415  if ( spec[1] == ':' ) // '::corefile' -- a core file
416  {
417  *outpkg = NULL;
418  *path = spec.substr( 2, std::string::npos );
419  }
420  else // ':pkgname:pkgfile' -- a packaged file
421  {
422  std::string::size_type second_colon = spec.find( ':', 2 );
423  if ( second_colon != std::string::npos )
424  {
425  std::string pkgname = spec.substr( 1, second_colon - 1 );
426  std::string pkgfile = spec.substr( second_colon + 1, std::string::npos );
427  Package* dstpkg = find_package( pkgname );
428  if ( dstpkg != NULL )
429  {
430  *outpkg = dstpkg;
431  *path = pkgfile;
432  }
433  else
434  {
435  ERROR_PRINT << "Unable to find package '" << pkgname << "'\n";
436  return false;
437  }
438  }
439  else
440  {
441  ERROR_PRINT << "Poorly formed packagefile descriptor: '" << spec << "'\n";
442  return false;
443  }
444  }
445  }
446  else
447  {
448  *outpkg = inpkg;
449  *path = spec;
450  }
451  return true;
452 }
453 
454 void load_packaged_cfgs( const char* cfgname, const char* taglist,
455  void ( *loadentry )( const Package*, Clib::ConfigElem& ) )
456 {
457  for ( const auto& pkg : systemstate.packages )
458  {
459  std::string filename = GetPackageCfgPath( pkg, cfgname );
460  if ( Clib::FileExists( filename.c_str() ) )
461  {
462  Clib::ConfigFile cf( filename.c_str(), taglist );
463  Clib::ConfigElem elem;
464 
465  while ( cf.read( elem ) )
466  {
467  loadentry( pkg, elem );
468  }
469  }
470  }
471 }
472 
473 void load_all_cfgs( const char* cfgname, const char* taglist,
474  void ( *loadentry )( const Package*, Clib::ConfigElem& ) )
475 {
476  std::string filename = std::string( "config/" ) + cfgname;
477  if ( Clib::FileExists( filename ) )
478  {
479  Clib::ConfigFile cf( filename.c_str(), taglist );
480  Clib::ConfigElem elem;
481 
482  while ( cf.read( elem ) )
483  {
484  loadentry( NULL, elem );
485  }
486  }
487  load_packaged_cfgs( cfgname, taglist, loadentry );
488 }
489 
490 
491 std::string GetPackageCfgPath( const Package* pkg, const std::string& filename )
492 {
493  std::string filepath;
494  if ( pkg == NULL )
495  { // If no package is sent, assume pol/config/file.xxx
496  filepath = "config/" + filename;
497  }
498  else
499  { // ** Going to save this feature for 097 **
500  // With packages, first try for /pkg/config/file.xxx
501  filepath = pkg->dir() + "config/" + filename;
502  if ( !Clib::FileExists( filepath ) )
503  {
504  // Lastly, assume /pkg/file.xxx
505  filepath = pkg->dir() + filename;
506  }
507  }
508 
509  return filepath;
510 }
511 }
512 }
std::string core_versionstring_required_
Definition: pkg.h:65
bool provides_system_home_page_
Definition: pkg.h:71
bool check_version2(const std::string &version_have, const std::string &version_need)
Definition: pkg.cpp:104
std::string remove_string(const char *propname)
Definition: cfgfile.cpp:381
void load_packaged_cfgs(const char *cfgname, const char *taglist, void(*loadentry)(const Package *, Clib::ConfigElem &))
Definition: pkg.cpp:454
SystemState systemstate
Definition: systemstate.cpp:12
std::string desc() const
Definition: pkg.cpp:176
std::string version
Definition: pkg.h:35
void check_package_deps()
Definition: pkg.cpp:377
bool provides_system_home_page() const
Definition: pkg.cpp:27
PackageList(Clib::ConfigElem &elem, const char *tag)
Definition: pkg.cpp:126
unsigned short core_required_
Definition: pkg.h:64
bool at_end() const
Definition: dirlist.cpp:102
const std::string & name() const
Definition: pkg.h:83
void check_dependencies() const
Definition: pkg.cpp:199
Package * find_package(const std::string &pkgname)
Definition: pkg.cpp:33
std::string name_
Definition: pkg.h:60
std::string version_
Definition: pkg.h:61
bool pkgdef_split(const std::string &spec, const Package *inpkg, const Package **outpkg, std::string *path)
Definition: pkg.cpp:410
size_t sizeEstimate() const
Definition: pkg.cpp:143
std::string read_string(const char *propname) const
Definition: cfgfile.cpp:395
PackageList replaces_
Definition: pkg.h:69
#define passert(exp)
Definition: passert.h:62
void check_deps_for_package(const Package *pkg)
Definition: pkg.cpp:351
void load_all_cfgs(const char *cfgname, const char *taglist, void(*loadentry)(const Package *, Clib::ConfigElem &))
Definition: pkg.cpp:473
std::string pkgname
Definition: pkg.h:34
bool check_replacements() const
Definition: pkg.cpp:181
bool remove_prop(const char *propname, std::string *value)
Definition: cfgfile.cpp:128
void compare_versions(const std::string &verleft, const std::string &verright, bool &isgreater, bool &isequal)
Definition: pkg.cpp:66
void replace_packages()
Definition: pkg.cpp:357
Package(const std::string &pkg_dir, Clib::ConfigElem &elem)
Definition: pkg.cpp:151
std::vector< Elem > elems
Definition: pkg.h:38
#define ISTRINGSTREAM
Definition: stlutil.h:73
unsigned short remove_ushort(const char *propname)
Definition: cfgfile.cpp:318
std::string dir_
Definition: pkg.h:59
std::string GetPackageCfgPath(const Package *pkg, const std::string &filename)
Definition: pkg.cpp:491
void check_conflicts() const
Definition: pkg.cpp:244
size_t estimateSize() const
Definition: pkg.cpp:258
PackageList conflicts_
Definition: pkg.h:68
#define ERROR_PRINT
Definition: logfacility.h:230
void mklower(std::string &str)
Definition: strutil.cpp:266
std::string normalized_dir_form(const std::string &istr)
Definition: fileutil.cpp:25
bool FileExists(const char *filename)
Definition: fileutil.cpp:118
bool read(ConfigElem &elem)
Definition: cfgfile.cpp:1015
#define INFO_PRINT
Definition: logfacility.h:223
const std::string & version() const
Definition: pkg.h:87
void load_package(const std::string &pkg_dir, Clib::ConfigElem &elem, bool quiet)
Definition: pkg.cpp:269
PackageList requires_
Definition: pkg.h:67
Definition: berror.cpp:12
PackagesByName packages_byname
Definition: systemstate.h:38
void test_check_version()
Definition: pkg.cpp:112
const std::string & dir() const
Definition: pkg.h:79
void remove_package(Package *pkg)
Definition: pkg.cpp:46
void load_packages(const std::string &basedir, bool quiet)
Definition: pkg.cpp:312