mirror of
https://github.com/polserver/polserver
synced 2026-08-13 08:23:08 -04:00
515 lines
14 KiB
C++
515 lines
14 KiB
C++
/** @file
|
|
*
|
|
* @par History
|
|
*/
|
|
|
|
|
|
#include "pkg.h"
|
|
|
|
#include "pol_global_config.h"
|
|
#include <algorithm>
|
|
#include <stdlib.h>
|
|
|
|
#include "../clib/cfgelem.h"
|
|
#include "../clib/cfgfile.h"
|
|
#include "../clib/clib.h"
|
|
#include "../clib/dirlist.h"
|
|
#include "../clib/fileutil.h"
|
|
#include "../clib/logfacility.h"
|
|
#include "../clib/passert.h"
|
|
#include "../clib/stlutil.h"
|
|
#include "../clib/strutil.h"
|
|
#include "systemstate.h"
|
|
|
|
namespace Pol
|
|
{
|
|
namespace Plib
|
|
{
|
|
bool Package::provides_system_home_page() const
|
|
{
|
|
return provides_system_home_page_;
|
|
}
|
|
|
|
|
|
Package* find_package( const std::string& pkgname )
|
|
{
|
|
auto itr = systemstate.packages_byname.find( pkgname );
|
|
if ( itr != systemstate.packages_byname.end() )
|
|
{
|
|
return ( *itr ).second;
|
|
}
|
|
else
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void remove_package( Package* pkg )
|
|
{
|
|
auto last = std::remove( systemstate.packages.begin(), systemstate.packages.end(), pkg );
|
|
systemstate.packages.erase( last, systemstate.packages.end() );
|
|
|
|
auto itr = systemstate.packages_byname.begin();
|
|
while ( itr != systemstate.packages_byname.end() )
|
|
{
|
|
if ( itr->second == pkg )
|
|
{
|
|
systemstate.packages_byname.erase( itr );
|
|
break;
|
|
}
|
|
++itr;
|
|
}
|
|
}
|
|
|
|
void compare_versions( const std::string& verleft, const std::string& verright, bool& isgreater,
|
|
bool& isequal )
|
|
{
|
|
isgreater = isequal = false;
|
|
|
|
const char* vneed = verright.c_str();
|
|
const char* vhave = verleft.c_str();
|
|
|
|
while ( ( vneed != nullptr && vneed[0] != '\0' ) || ( vhave != nullptr && vhave[0] != '\0' ) )
|
|
{
|
|
char* new_vneed = nullptr;
|
|
char* new_vhave = nullptr;
|
|
unsigned int vneedpart, vhavepart;
|
|
vneedpart = ( vneed != nullptr ) ? strtoul( vneed, &new_vneed, 0 ) : 0;
|
|
vhavepart = ( vhave != nullptr ) ? strtoul( vhave, &new_vhave, 0 ) : 0;
|
|
|
|
if ( vhavepart > vneedpart )
|
|
{
|
|
isgreater = true;
|
|
return;
|
|
}
|
|
else if ( vhavepart < vneedpart )
|
|
{
|
|
return;
|
|
}
|
|
else // same, so check the next one
|
|
{
|
|
vneed = new_vneed;
|
|
if ( vneed && *vneed )
|
|
++vneed;
|
|
vhave = new_vhave;
|
|
if ( vhave && *vhave )
|
|
++vhave;
|
|
}
|
|
}
|
|
isequal = true;
|
|
}
|
|
|
|
bool check_version2( const std::string& version_have, const std::string& version_need )
|
|
{
|
|
bool isgreater;
|
|
bool isequal;
|
|
compare_versions( version_have, version_need, isgreater, isequal );
|
|
return ( isequal || isgreater );
|
|
}
|
|
|
|
void test_check_version()
|
|
{ // have // need
|
|
passert( check_version2( "0", "0" ) == true );
|
|
passert( check_version2( "1", "0" ) == true );
|
|
passert( check_version2( "0", "1" ) == false );
|
|
passert( check_version2( "0.5", "1" ) == false );
|
|
passert( check_version2( "0.5", "0" ) == true );
|
|
passert( check_version2( "1.2", "1.12" ) == false );
|
|
passert( check_version2( "1.12", "1.2" ) == true );
|
|
passert( check_version2( "1.2.3", "1" ) == true );
|
|
passert( check_version2( "1.1", "1.2.3" ) == false );
|
|
passert( check_version2( "1.3", "1.2.3" ) == true );
|
|
}
|
|
|
|
PackageList::PackageList( Clib::ConfigElem& elem, const char* tag )
|
|
{
|
|
std::string tmp;
|
|
while ( elem.remove_prop( tag, &tmp ) )
|
|
{
|
|
Clib::mklowerASCII( tmp );
|
|
ISTRINGSTREAM is( tmp );
|
|
Elem _elem;
|
|
if ( is >> _elem.pkgname )
|
|
{
|
|
if ( !( is >> _elem.version ) )
|
|
_elem.version = "0";
|
|
|
|
elems.push_back( _elem );
|
|
}
|
|
}
|
|
}
|
|
size_t PackageList::sizeEstimate() const
|
|
{
|
|
size_t size = sizeof( PackageList );
|
|
for ( const auto& elem : elems )
|
|
size += elem.pkgname.capacity() + elem.version.capacity();
|
|
return size;
|
|
}
|
|
|
|
Package::Package( const std::string& pkg_dir, Clib::ConfigElem& elem )
|
|
: dir_( pkg_dir ),
|
|
name_( elem.remove_string( "Name" ) ),
|
|
version_( elem.remove_string( "Version", "0" ) ),
|
|
core_required_( 0 ),
|
|
requires_( elem, "Requires" ),
|
|
conflicts_( elem, "Conflicts" ),
|
|
replaces_( elem, "Replaces" ),
|
|
provides_system_home_page_( elem.remove_bool( "ProvidesSystemHomePage", false ) )
|
|
{
|
|
Clib::mklowerASCII( name_ );
|
|
// CoreRequired can either be a number (94,,95 etc)
|
|
// or a version string (POL095-2003-01-28)
|
|
std::string tmp = elem.read_string( "CoreRequired", "0" );
|
|
if ( isdigit( tmp[0] ) )
|
|
{
|
|
// the first kind - a number.
|
|
core_required_ = elem.remove_ushort( "CoreRequired", 0 );
|
|
}
|
|
else
|
|
{
|
|
core_versionstring_required_ = elem.remove_string( "CoreRequired" );
|
|
}
|
|
}
|
|
|
|
std::string Package::desc() const
|
|
{
|
|
return name() + " (" + dir() + ")";
|
|
}
|
|
|
|
bool Package::check_replacements() const
|
|
{
|
|
bool any = false;
|
|
for ( const auto& elem : replaces_.elems )
|
|
{
|
|
Package* found = find_package( elem.pkgname );
|
|
if ( found != nullptr )
|
|
{
|
|
any = true;
|
|
INFO_PRINT << "Package " << desc() << " replaces package " << found->desc() << "\n";
|
|
remove_package( found );
|
|
delete found;
|
|
found = nullptr;
|
|
}
|
|
}
|
|
return any;
|
|
}
|
|
|
|
void Package::check_dependencies() const
|
|
{
|
|
if ( core_required_ )
|
|
{
|
|
if ( core_required_ > POL_VERSION ) // TODO: use a more fine grained check here
|
|
{
|
|
ERROR_PRINT << "Error in package " << desc() << ":\n"
|
|
<< " Core version " << core_required_ << " is required, but version "
|
|
<< POL_VERSION << " is running.\n";
|
|
throw std::runtime_error( "Package requires a newer core version" );
|
|
}
|
|
}
|
|
else if ( !core_versionstring_required_.empty() )
|
|
{
|
|
int cmp = stricmp( POL_VERSION_ID, core_versionstring_required_.c_str() );
|
|
if ( cmp < 0 )
|
|
{
|
|
ERROR_PRINT << "Error in package " << desc() << ":\n"
|
|
<< " Core version " << core_versionstring_required_
|
|
<< " is required, but version " << POL_VERSION_ID << " is running.\n";
|
|
throw std::runtime_error( "Package requires a newer core version" );
|
|
}
|
|
}
|
|
for ( const auto& elem : requires_.elems )
|
|
{
|
|
Package* found = find_package( elem.pkgname );
|
|
if ( found == nullptr )
|
|
{
|
|
ERROR_PRINT << "Error in package '" << name_ << "' (" << dir_ << "):\n"
|
|
<< " Package '" << elem.pkgname << "' is required, but is not installed.\n";
|
|
throw std::runtime_error( "Package dependency error" );
|
|
}
|
|
else
|
|
{
|
|
if ( !check_version2( found->version_, elem.version ) )
|
|
{
|
|
ERROR_PRINT << "Error in package '" << name_ << "' (" << dir_ << "):\n"
|
|
<< " Package '" << elem.pkgname << "' version " << elem.version
|
|
<< " is required, but version " << found->version_ << " was found\n";
|
|
throw std::runtime_error( "Package dependency error" );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Package::check_conflicts() const
|
|
{
|
|
for ( const auto& elem : conflicts_.elems )
|
|
{
|
|
Package* found = find_package( elem.pkgname );
|
|
if ( found != nullptr )
|
|
{
|
|
ERROR_PRINT << "Error in package " << desc() << ":\n"
|
|
<< " Package conflicts with package " << found->desc() << "\n";
|
|
throw std::runtime_error( "Package dependency error" );
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t Package::estimateSize() const
|
|
{
|
|
size_t size = dir_.capacity() + name_.capacity() + version_.capacity() +
|
|
sizeof( unsigned short ) /*core_required*/
|
|
+ core_versionstring_required_.capacity() + requires_.sizeEstimate() +
|
|
conflicts_.sizeEstimate() + replaces_.sizeEstimate() +
|
|
sizeof( bool ) /*provides_system_home_page_*/
|
|
;
|
|
return size;
|
|
}
|
|
|
|
void load_package( const std::string& pkg_dir, Clib::ConfigElem& elem, bool quiet )
|
|
{
|
|
std::unique_ptr<Package> pkg( new Package( pkg_dir, elem ) );
|
|
Package* existing_pkg = find_package( pkg->name() );
|
|
|
|
if ( existing_pkg != nullptr )
|
|
{
|
|
bool isgreater, isequal;
|
|
compare_versions( pkg->version(), existing_pkg->version(), isgreater, isequal );
|
|
if ( isgreater )
|
|
{
|
|
// replace existing package with newer version
|
|
if ( !quiet )
|
|
INFO_PRINT << "Replacing package " << existing_pkg->desc() << " version "
|
|
<< existing_pkg->version() << " with version " << pkg->version() << " found in "
|
|
<< pkg->dir() << "\n";
|
|
remove_package( existing_pkg );
|
|
delete existing_pkg;
|
|
existing_pkg = nullptr;
|
|
}
|
|
else if ( isequal )
|
|
{
|
|
ERROR_PRINT << "Error in package " << pkg->desc() << ":\n"
|
|
<< " Package by same name already found in " << existing_pkg->desc() << "\n";
|
|
throw std::runtime_error( "Duplicate package found" );
|
|
}
|
|
else
|
|
{
|
|
// skip this package, its version is older
|
|
if ( !quiet )
|
|
INFO_PRINT << "Skipping package " << pkg->desc() << " version " << pkg->version()
|
|
<< " because version " << existing_pkg->version() << " was already found in "
|
|
<< existing_pkg->dir() << "\n";
|
|
return;
|
|
}
|
|
}
|
|
|
|
systemstate.packages.push_back( pkg.get() );
|
|
Package* ppkg = pkg.release();
|
|
systemstate.packages_byname.insert( PackagesByName::value_type( ppkg->name(), ppkg ) );
|
|
}
|
|
|
|
|
|
void load_packages( const std::string& basedir, bool quiet )
|
|
{
|
|
for ( Clib::DirList dl( basedir.c_str() ); !dl.at_end(); dl.next() )
|
|
{
|
|
std::string dirname = dl.name();
|
|
if ( dirname[0] == '.' )
|
|
continue;
|
|
if ( dirname == "template" )
|
|
continue;
|
|
|
|
std::string pkg_dir = basedir + dirname + "/";
|
|
std::string pkg_cfg = pkg_dir + "pkg.cfg";
|
|
|
|
if ( Clib::FileExists( pkg_cfg.c_str() ) )
|
|
{
|
|
Clib::ConfigFile cf( pkg_cfg.c_str() );
|
|
Clib::ConfigElem elem;
|
|
|
|
cf.readraw( elem );
|
|
std::string enabled_pkg = pkg_dir + "enabled.pkg";
|
|
std::string disabled_pkg = pkg_dir + "disabled.pkg";
|
|
|
|
if ( ( elem.remove_bool( "Enabled" ) == true || Clib::FileExists( enabled_pkg.c_str() ) ) &&
|
|
!Clib::FileExists( disabled_pkg.c_str() ) )
|
|
{
|
|
if ( !quiet )
|
|
INFO_PRINT << "Loading package in " << pkg_dir << "\n";
|
|
load_package( pkg_dir, elem, quiet );
|
|
|
|
load_packages( pkg_dir, quiet );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
load_packages( pkg_dir, quiet );
|
|
}
|
|
}
|
|
}
|
|
|
|
void check_deps_for_package( const Package* pkg )
|
|
{
|
|
pkg->check_dependencies();
|
|
pkg->check_conflicts();
|
|
}
|
|
|
|
void replace_packages()
|
|
{
|
|
bool done;
|
|
do
|
|
{
|
|
done = true;
|
|
|
|
for ( const auto& pkg : systemstate.packages )
|
|
{
|
|
bool change = pkg->check_replacements();
|
|
if ( change )
|
|
{
|
|
done = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
} while ( !done );
|
|
}
|
|
|
|
void check_package_deps()
|
|
{
|
|
for ( const auto& pkg : systemstate.packages )
|
|
check_deps_for_package( pkg );
|
|
}
|
|
|
|
void load_packages( bool quiet )
|
|
{
|
|
test_check_version();
|
|
|
|
load_packages( "pkg/", quiet );
|
|
|
|
if ( Clib::FileExists( "config/pkgroots.cfg" ) )
|
|
{
|
|
Clib::ConfigFile cf( "config/pkgroots.cfg", "PackageRoot" );
|
|
Clib::ConfigElem elem;
|
|
while ( cf.read( elem ) )
|
|
{
|
|
std::string dir;
|
|
while ( elem.remove_prop( "dir", &dir ) )
|
|
{
|
|
dir = Clib::normalized_dir_form( dir );
|
|
if ( !quiet )
|
|
INFO_PRINT << "Searching for packages under " << dir << "\n";
|
|
load_packages( dir.c_str(), quiet );
|
|
}
|
|
}
|
|
}
|
|
|
|
replace_packages();
|
|
|
|
check_package_deps();
|
|
// sort pkg vector by name, so e.g. startup order is in a defined and maybe also expected order.
|
|
std::sort(
|
|
systemstate.packages.begin(), systemstate.packages.end(),
|
|
[]( const Package* pkg1, const Package* pkg2 ) { return pkg1->name() < pkg2->name(); } );
|
|
}
|
|
|
|
bool pkgdef_split( const std::string& spec, const Package* inpkg, const Package** outpkg,
|
|
std::string* path )
|
|
{
|
|
if ( spec[0] == ':' )
|
|
{
|
|
if ( spec[1] == ':' ) // '::corefile' -- a core file
|
|
{
|
|
*outpkg = nullptr;
|
|
*path = spec.substr( 2, std::string::npos );
|
|
}
|
|
else // ':pkgname:pkgfile' -- a packaged file
|
|
{
|
|
std::string::size_type second_colon = spec.find( ':', 2 );
|
|
if ( second_colon != std::string::npos )
|
|
{
|
|
std::string pkgname = spec.substr( 1, second_colon - 1 );
|
|
std::string pkgfile = spec.substr( second_colon + 1, std::string::npos );
|
|
Package* dstpkg = find_package( pkgname );
|
|
if ( dstpkg != nullptr )
|
|
{
|
|
*outpkg = dstpkg;
|
|
*path = pkgfile;
|
|
}
|
|
else
|
|
{
|
|
ERROR_PRINT << "Unable to find package '" << pkgname << "'\n";
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_PRINT << "Poorly formed packagefile descriptor: '" << spec << "'\n";
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*outpkg = inpkg;
|
|
*path = spec;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void load_packaged_cfgs( const char* cfgname, const char* taglist,
|
|
void ( *loadentry )( const Package*, Clib::ConfigElem& ) )
|
|
{
|
|
for ( const auto& pkg : systemstate.packages )
|
|
{
|
|
std::string filename = GetPackageCfgPath( pkg, cfgname );
|
|
if ( Clib::FileExists( filename.c_str() ) )
|
|
{
|
|
Clib::ConfigFile cf( filename.c_str(), taglist );
|
|
Clib::ConfigElem elem;
|
|
|
|
while ( cf.read( elem ) )
|
|
{
|
|
loadentry( pkg, elem );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void load_all_cfgs( const char* cfgname, const char* taglist,
|
|
void ( *loadentry )( const Package*, Clib::ConfigElem& ) )
|
|
{
|
|
std::string filename = std::string( "config/" ) + cfgname;
|
|
if ( Clib::FileExists( filename ) )
|
|
{
|
|
Clib::ConfigFile cf( filename.c_str(), taglist );
|
|
Clib::ConfigElem elem;
|
|
|
|
while ( cf.read( elem ) )
|
|
{
|
|
loadentry( nullptr, elem );
|
|
}
|
|
}
|
|
load_packaged_cfgs( cfgname, taglist, loadentry );
|
|
}
|
|
|
|
|
|
std::string GetPackageCfgPath( const Package* pkg, const std::string& filename )
|
|
{
|
|
std::string filepath;
|
|
if ( pkg == nullptr )
|
|
{ // If no package is sent, assume pol/config/file.xxx
|
|
filepath = "config/" + filename;
|
|
}
|
|
else
|
|
{ // ** Going to save this feature for 097 **
|
|
// With packages, first try for /pkg/config/file.xxx
|
|
filepath = pkg->dir() + "config/" + filename;
|
|
if ( !Clib::FileExists( filepath ) )
|
|
{
|
|
// Lastly, assume /pkg/file.xxx
|
|
filepath = pkg->dir() + filename;
|
|
}
|
|
}
|
|
|
|
return filepath;
|
|
}
|
|
} // namespace Plib
|
|
} // namespace Pol
|