#
SET(LIBZYPP_MAJOR "17")
SET(LIBZYPP_COMPATMINOR "2")
-SET(LIBZYPP_MINOR "2")
-SET(LIBZYPP_PATCH "2")
+SET(LIBZYPP_MINOR "3")
+SET(LIBZYPP_PATCH "0")
#
-# LAST RELEASED: 17.2.2 (2)
+# LAST RELEASED: 17.3.0 (2)
# (The number in parenthesis is LIBZYPP_COMPATMINOR)
#=======
-------------------------------------------------------------------
+Mon Apr 9 13:11:50 CEST 2018 - ma@suse.de
+
+- Show progressbar when running posttrans scripts
+- Execute service plugin script chrooted to the RepoManagers root
+ (bsc#1080693)
+- Make sure the product file comes from /etc/products.d for the
+ fallback product search (bsc#1086602)
+- Introduce ZConfig::repoManagerRoot to support having diverging
+ target and repomanager root paths
+- Rename `stderr` as it can be a macro (fixes #102)
+- version 17.3.0 (2)
+
+-------------------------------------------------------------------
Tue Mar 13 18:03:42 CET 2018 - ma@suse.de
- Protect code against broken translations (bsc#1082711)
ContentType
CpeId
Date
+ DrunkenBishop
Dup
Digest
Deltarpm
--- /dev/null
+#include <boost/test/auto_unit_test.hpp>
+#include <iostream>
+#include "zypp/base/DrunkenBishop.h"
+
+using boost::unit_test::test_case;
+
+using namespace std;
+using namespace zypp;
+using base::DrunkenBishop;
+
+BOOST_AUTO_TEST_CASE(drunkenbishop)
+{
+ {
+ DrunkenBishop b;
+ BOOST_CHECK_EQUAL( b.asString(),
+ "++\n"
+ "++" );
+ }
+ {
+ DrunkenBishop b( "94", 0, 0 );
+ BOOST_CHECK_EQUAL( b.asString(),
+ "+-+\n"
+ "|E|\n"
+ "+-+" );
+ }
+ {
+ BOOST_CHECK_THROW( DrunkenBishop( "9g" ), std::invalid_argument );
+ }
+ {
+ DrunkenBishop b( "" );
+ BOOST_CHECK_EQUAL( b.asString(),
+ "+-----------------+\n"
+ "| |\n"
+ "| |\n"
+ "| |\n"
+ "| |\n"
+ "| E |\n"
+ "| |\n"
+ "| |\n"
+ "| |\n"
+ "| |\n"
+ "+-----------------+" );
+ }
+ {
+ DrunkenBishop b( "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", "Title" );
+ BOOST_CHECK_EQUAL( b.asString(),
+ "+-----[Title]-----+\n"
+ "| |\n"
+ "| |\n"
+ "| |\n"
+ "| ^ |\n"
+ "| E |\n"
+ "| |\n"
+ "| |\n"
+ "| |\n"
+ "| |\n"
+ "+---[CCCCCCCC]----+" );
+ }
+ {
+ DrunkenBishop b( "9c6fb17fa201ad829d808739379a2a51", "very very long Title" );
+ BOOST_CHECK_EQUAL( b.asString(),
+ "+[very very long ]+\n"
+ "| |\n"
+ "| |\n"
+ "| |\n"
+ "| E+ . o |\n"
+ "| .= = S o |\n"
+ "|. * = . + o |\n"
+ "| .o . + . = |\n"
+ "|.. . . o. . |\n"
+ "|o ...o |\n"
+ "+---[379A2A51]----+" );
+ }
+ {
+ DrunkenBishop b( "4E98E67519D98DC7362A5990E3A5C360307E3D54" );
+ BOOST_CHECK_EQUAL( b.asString(),
+ "+-------------------+\n"
+ "| ^. .^E |\n"
+ "| . .^^: . |\n"
+ "| ...:^? : |\n"
+ "| . ?.: i |\n"
+ "| ^ i : . |\n"
+ "| : S l . |\n"
+ "| ^ : . . |\n"
+ "| . . |\n"
+ "| |\n"
+ "| |\n"
+ "| |\n"
+ "+----[307E3D54]-----+" );
+ }
+}
// providing a file which does not exist should throw
BOOST_CHECK_THROW(setaccess.provideFile("/testBADNAME.txt"), media::MediaFileNotFoundException);
+
+ Pathname fPath;
+ {
+ Url url = web.url();
+ url.setPathName("/testBADNAME.txt");
+
+ // providing a file which does not exist should throw
+ BOOST_CHECK_THROW(MediaSetAccess::provideFileFromUrl(url), media::MediaFileNotFoundException);
+
+ url.setPathName("/test.txt");
+
+ //providing a file by static method, file should exist after method call
+ ManagedFile file = MediaSetAccess::provideFileFromUrl( url );
+ fPath = file;
+ BOOST_CHECK(check_file_exists(fPath) == true);
+ }
+ //file should be removed once the ManagedFile goes out of scope
+ BOOST_CHECK(check_file_exists(fPath) == false);
+
web.stop();
}
BOOST_CHECK_EQUAL(Pathname("cc:a/b").asString(), "./cc:a/b" );
}
+BOOST_AUTO_TEST_CASE(pathname_stripprefix)
+{
+ BOOST_CHECK_EQUAL( Pathname::stripprefix( "", "" ), "" );
+ BOOST_CHECK_EQUAL( Pathname::stripprefix( "", "/" ), "/" );
+ BOOST_CHECK_EQUAL( Pathname::stripprefix( "", "/foo" ), "/foo" );
+ BOOST_CHECK_EQUAL( Pathname::stripprefix( "/", "" ), "" );
+ BOOST_CHECK_EQUAL( Pathname::stripprefix( "/", "/" ), "/" );
+ BOOST_CHECK_EQUAL( Pathname::stripprefix( "/", "/foo" ), "/foo" );
+ BOOST_CHECK_EQUAL( Pathname::stripprefix( "/f", "" ), "" );
+ BOOST_CHECK_EQUAL( Pathname::stripprefix( "/f", "/" ), "/" );
+ BOOST_CHECK_EQUAL( Pathname::stripprefix( "/f", "/foo" ), "/foo" );
+ BOOST_CHECK_EQUAL( Pathname::stripprefix( "/foo", "" ), "" );
+ BOOST_CHECK_EQUAL( Pathname::stripprefix( "/foo", "/" ), "/" );
+ BOOST_CHECK_EQUAL( Pathname::stripprefix( "/foo", "/foo" ), "/" );
+ BOOST_CHECK_EQUAL( Pathname::stripprefix( "/foo", "/foo/baa" ), "/baa" );
+}
TmpDir tmpCachePath;
RepoManagerOptions opts( RepoManagerOptions::makeTestSetup( tmpCachePath ) ) ;
- filesystem::assert_dir( opts.knownReposPath );
- filesystem::assert_dir( opts.pluginsPath / "services");
+ opts.rootDir = ""; // NOTE: After all paths have been setup correctly,
+ // we must reset the RepoManager rootDir to prevent the plugin script
+ // from being executed chrooted (would require UID 0).
opts.pluginsPath = DATADIR + "/plugin-service-lib-1";
BOOST_CHECK(PathInfo(opts.pluginsPath / "services/service").isExist());
namespace opt = boost::program_options;
#include <zypp/target/rpm/RpmDb.h>
+#include <zypp/base/String.h>
static std::string appname( "unknown" );
return lhs.gpgPubkeyRelease() > rhs.gpgPubkeyRelease(); // intentionally reverse cdate
}
+std::ostream & dumpPubkeyOn( std::ostream & str, const PublicKey & key_r )
+{
+ std::vector<std::string> art( key_r.asciiArt().asLines( " ", PublicKey::AsciiArt::USE_COLOR ) );
+
+ std::vector<std::string> info;
+ str::split( (str::Str() << dump(key_r)).str(), std::back_inserter( info ), "\n" );
+
+ for ( unsigned i = 1; i < info.size(); ++i )
+ art[i] += info[i];
+
+ str << info[0] << endl;
+ for ( const auto & line : art )
+ str << line << endl;
+ return str << endl;
+}
+
/******************************************************************
**
** FUNCTION NAME : main
std::list<PublicKey> rpmpubkeys( rpmdb.pubkeys() );
rpmpubkeys.sort( byTTL );
+
+
if ( ! vm.count( "key-file" ) )
{
std::string last;
cout << *it << endl;
else
{
- cout << dump( *it ) << endl;
+ dumpPubkeyOn( cout, *it );
last = it->gpgPubkeyVersion();
}
}
{
cout << "=== " << PathInfo(*it) << endl;
PublicKey pubkey( *it );
- cout << dump( pubkey ) << endl;
+ dumpPubkeyOn( cout, pubkey );
std::string pubkeyV( pubkey.gpgPubkeyVersion() );
std::string pubkeyR( pubkey.gpgPubkeyRelease() );
SET( zypp_base_SRCS
base/InterProcessMutex.cc
base/Backtrace.cc
+ base/DrunkenBishop.cc
base/SerialNumber.cc
base/Random.cc
base/Measure.cc
base/InterProcessMutex.h
base/Backtrace.h
base/Collector.h
+ base/DrunkenBishop.h
base/SerialNumber.h
base/Easy.h
base/Errno.h
EarlyPipe();
~EarlyPipe();
void closeW() { if ( _fds[W] != -1 ) { ::close( _fds[W] ); _fds[W] = -1; } }
- FILE * stderr() { return _stderr; }
+ FILE * fStdErr() { return _stderr; }
protected:
FILE * _stderr;
int _fds[2];
class ExternalProgramWithStderr : private externalprogram::EarlyPipe, public ExternalProgram
{
public:
- ExternalProgramWithStderr( const Arguments & argv_r )
- : ExternalProgram( argv_r, Stderr_To_FileDesc, /*use_pty*/false, _fds[W] )
+ ExternalProgramWithStderr( const Arguments & argv_r, bool defaultLocale_r = false, const Pathname & root_r = "" )
+ : ExternalProgram( argv_r, Stderr_To_FileDesc, /*use_pty*/false, _fds[W], defaultLocale_r, root_r )
{ _initStdErr(); }
+ /** \overlocad Convenience taking just the \a root_r. */
+ ExternalProgramWithStderr( const Arguments & argv_r, const Pathname & root_r )
+ : ExternalProgramWithStderr( argv_r, false, root_r )
+ {}
- ExternalProgramWithStderr( const Arguments & argv_r, const Environment & environment_r )
- : ExternalProgram( argv_r, environment_r, Stderr_To_FileDesc, /*use_pty*/false, _fds[W] )
+ ExternalProgramWithStderr( const Arguments & argv_r, const Environment & environment_r, bool defaultLocale_r = false, const Pathname & root_r = "" )
+ : ExternalProgram( argv_r, environment_r, Stderr_To_FileDesc, /*use_pty*/false, _fds[W], defaultLocale_r, root_r )
{ _initStdErr(); }
-
- public:
+ /** \overlocad Convenience taking just the \a root_r. */
+ ExternalProgramWithStderr( const Arguments & argv_r, const Environment & environment_r, const Pathname & root_r )
+ : ExternalProgramWithStderr( argv_r, environment_r, false, root_r )
+ {}
+ public:
/** Return \c FILE* to read programms stderr (O_NONBLOCK set). */
- using externalprogram::EarlyPipe::stderr;
+ using externalprogram::EarlyPipe::fStdErr;
/** Read data up to \c delim_r from stderr (nonblocking).
* \note If \c delim_r is '\0', we read as much data as possible.
bool verifyFileTrustedSignature( const Pathname & file, const Pathname & signature )
{ return verifyFile( file, signature, trustedKeyRing() ); }
+ PublicKeyData trustedPublicKeyExists( const std::string & id )
+ { return publicKeyExists(id, trustedKeyRing());}
+
private:
bool verifyFile( const Pathname & file, const Pathname & signature, const Pathname & keyring );
void importKey( const Pathname & keyfile, const Pathname & keyring );
std::list<PublicKeyData> KeyRing::trustedPublicKeyData()
{ return _pimpl->trustedPublicKeyData(); }
+ PublicKeyData KeyRing::trustedPublicKeyData(const std::string &id_r)
+ { return _pimpl->trustedPublicKeyExists( id_r ); }
+
bool KeyRing::verifyFileSignatureWorkflow( const Pathname & file, const std::string & filedesc, const Pathname & signature, bool & sigValid_r, const KeyContext & keycontext )
{ return _pimpl->verifyFileSignatureWorkflow( file, filedesc, signature, sigValid_r, keycontext ); }
std::list<PublicKeyData> trustedPublicKeyData();
/**
+ * Get a trusted public key's data in the keyring (key data only)
+ */
+ PublicKeyData trustedPublicKeyData( const std::string &id );
+
+ /**
* Follows a signature verification interacting with the user.
* The bool returned depends on user decision to trust or not.
*
#include "zypp/ZYppCallbacks.h"
#include "zypp/MediaSetAccess.h"
#include "zypp/PathInfo.h"
+#include "zypp/TmpPath.h"
//#include "zypp/source/MediaSetAccessReportReceivers.h"
using namespace std;
return op.result;
}
+ ManagedFile MediaSetAccess::provideFileFromUrl(const Url &file_url, ProvideFileOptions options)
+ {
+ Url url(file_url);
+ Pathname path(url.getPathName());
+ url.setPathName ("/");
+ MediaSetAccess access(url);
+
+ ManagedFile tmpFile = filesystem::TmpFile::asManagedFile();
+
+ Pathname file = access.provideFile(path, 1, options);
+
+ //prevent the file from being deleted when MediaSetAccess gets out of scope
+ if ( filesystem::hardlinkCopy(file, tmpFile) != 0 )
+ ZYPP_THROW(Exception("Can't copy file from " + file.asString() + " to " + tmpFile->asString() ));
+
+ return tmpFile;
+ }
+
Pathname MediaSetAccess::provideOptionalFile( const Pathname & file, unsigned media_nr )
{
try
#include "zypp/Pathname.h"
#include "zypp/CheckSum.h"
#include "zypp/OnMediaLocation.h"
+#include "zypp/ManagedFile.h"
///////////////////////////////////////////////////////////////////
namespace zypp
Pathname provideFile(const Pathname & file, unsigned media_nr = 1, ProvideFileOptions options = PROVIDE_DEFAULT );
/**
+ * Provides \a file from \a url.
+ *
+ * \param absolute url to the file
+ * \return local pathname of the requested file
+ *
+ * \note interaction with the user does not ocurr if
+ * \ref ProvideFileOptions::NON_INTERACTIVE is set.
+ *
+ * \throws MediaException if a problem occured and user has chosen to
+ * abort the operation. The calling code should take care
+ * to quit the current operation.
+ * \throws SkipRequestException if a problem occured and user has chosen
+ * to skip the current operation. The calling code should continue
+ * with the next one, if possible.
+ * \see zypp::media::MediaManager::provideFile()
+ */
+ static ManagedFile provideFileFromUrl( const Url & file_url, ProvideFileOptions options = PROVIDE_DEFAULT );
+
+ /**
* Provides an optional \a file from media \a media_nr.
*
* Like \ref provideFile (NON_INTERACTIVE), but return an empty \ref Pathname
return root_r / path_r;
}
+ Pathname Pathname::stripprefix( const Pathname & root_r, const Pathname & path_r )
+ {
+ if ( root_r.emptyOrRoot() )
+ return path_r;
+ if ( root_r == path_r )
+ return "/";
+ std::string rest( str::stripPrefix( path_r.asString(), root_r.asString() ) );
+ if ( rest[0] == '/' ) // needs to be a dir prefix!
+ return std::move(rest);
+ return path_r;
+ }
+
///////////////////////////////////////////////////////////////////
//
// METHOD NAME : Pathname::cat
/** Return \c path_r prefixed with \c root_r, unless it is already prefixed. */
static Pathname assertprefix( const Pathname & root_r, const Pathname & path_r );
+ /** Return \c path_r with any \c root_r dir prefix striped. */
+ static Pathname stripprefix( const Pathname & root_r, const Pathname & path_r );
+
/** Concatenation of pathnames.
* \code
* "foo" / "baa" ==> "foo/baa"
// bnc#784900: for installed products check whether the file is owned by
// some package. If so, ust this as buddy.
sat::LookupAttr q( sat::SolvAttr::filelist, repository() );
- std::string refFile( referenceFilename() );
+ std::string refFile( referenceFilename() ); // the basename only!
if ( ! refFile.empty() )
{
- StrMatcher matcher( referenceFilename() );
+ StrMatcher matcher( "/etc/products.d/"+refFile, Match::STRING | Match::FILES );
q.setStrMatcher( matcher );
if ( ! q.empty() )
found = q.begin().inSolvable();
bool PublicKeyData::providesKey( const std::string & id_r ) const
{ return( id_r == _pimpl->_id || _pimpl->hasSubkeyId( id_r ) ); }
+ PublicKeyData::AsciiArt PublicKeyData::asciiArt() const
+ { return AsciiArt( fingerprint() /* TODO: key algorithm could be added as top tile. */ ); }
+
std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj )
{
str << "[" << obj.name() << "]" << endl;
#include "zypp/base/Iterable.h"
#include "zypp/base/PtrTypes.h"
#include "zypp/base/Exception.h"
+#include "zypp/base/DrunkenBishop.h"
#include "zypp/Pathname.h"
#include "zypp/Edition.h"
#include "zypp/Date.h"
/** Whether \a id_r is the \ref id of the primary key or of a subkey. */
bool providesKey( const std::string & id_r ) const;
+ public:
+ /** Random art fingerprint visualization type (\ref base::DrunkenBishop). */
+ typedef base::DrunkenBishop AsciiArt;
+
+ /** Random art fingerprint visualization (\ref base::DrunkenBishop).
+ * \code
+ * PublicKeyData key;
+ * cout << key.asciiArt( PublicKey::AsciiArt::USE_COLOR ) << endl;
+ * \endcode
+ */
+ AsciiArt asciiArt() const;
+
private:
class Impl;
RWCOW_pointer<Impl> _pimpl;
{ return keyData().providesKey( id_r ); }
public:
+ typedef PublicKeyData::AsciiArt AsciiArt; ///!< \see \ref PublicKeyData
+
+ AsciiArt asciiArt() const ///!< \see \ref PublicKeyData
+ { return keyData().asciiArt(); }
+
+ public:
/** File containig the ASCII armored key. */
Pathname path() const;
std::list<RepoInfo> readRepoFile( const Url & repo_file )
{
- // no interface to download a specific file, using workaround:
- //! \todo add MediaManager::provideFile(Url file_url) to easily access any file URLs? (no need for media access id or media_nr)
- Url url(repo_file);
- Pathname path(url.getPathName());
- url.setPathName ("/");
- MediaSetAccess access(url);
- Pathname local = access.provideFile(path);
+ ManagedFile local = MediaSetAccess::provideFileFromUrl(repo_file);
DBG << "reading repo file " << repo_file << ", local path: " << local << endl;
}
}
- repo::PluginServices(_options.pluginsPath/"services", ServiceCollector(_services));
+ repo::PluginServices(_options.pluginsPath/"services", ServiceCollector(_services));
}
///////////////////////////////////////////////////////////////////
// and in zypper.
std::pair<DefaultIntegral<bool,false>, repo::ServicePluginInformalException> uglyHack;
try {
- ServiceRepos( service, bind( &RepoCollector::collect, &collector, _1 ) );
+ // FIXME bsc#1080693: Shortcoming of (plugin)services (and repos as well) is that they
+ // are not aware of the RepoManagers rootDir. The service url, as created in known_services,
+ // contains the full path to the script. The script however has to be executed chrooted.
+ // Repos would need to know the RepoMangers rootDir to use the correct vars.d to replace
+ // repos variables. Until RepoInfoBase is aware if the rootDir, we need to explicitly pass it
+ // to ServiceRepos.
+ ServiceRepos( _options.rootDir, service, bind( &RepoCollector::collect, &collector, _1 ) );
}
catch ( const repo::ServicePluginInformalException & e )
{
return ret;
}
+ ManagedFile TmpFile::asManagedFile()
+ {
+ filesystem::TmpFile tmpFile;
+ ManagedFile mFile ( tmpFile.path(), filesystem::unlink );
+ tmpFile.autoCleanup(false); //cleaned up by ManagedFile
+ return mFile;
+ }
+
///////////////////////////////////////////////////////////////////
//
// METHOD NAME : TmpFile::defaultPrefix
#include "zypp/Pathname.h"
#include "zypp/base/PtrTypes.h"
+#include "zypp/ManagedFile.h"
namespace zypp {
namespace filesystem {
*/
static TmpFile makeSibling( const Pathname & sibling_r );
+ /**
+ * Create a temporary file and convert it to a automatically
+ * cleaned up ManagedFile
+ */
+ static ManagedFile asManagedFile ();
+
public:
/**
* @return The default prefix for temporary files (TmpFile.)
Pathname cfg_known_repos_path;
Pathname cfg_known_services_path;
Pathname cfg_vars_path;
+ Pathname cfg_repo_mgr_root_path;
Pathname cfg_vendor_path;
Pathname cfg_multiversion_path;
Pathname ZConfig::systemRoot() const
{ return _autodetectSystemRoot(); }
+
+ Pathname ZConfig::repoManagerRoot() const
+ {
+ return ( _pimpl->cfg_repo_mgr_root_path.empty()
+ ? systemRoot() : _pimpl->cfg_repo_mgr_root_path );
+ }
+
+ void ZConfig::setRepoManagerRoot(const zypp::filesystem::Pathname &root)
+ { _pimpl->cfg_repo_mgr_root_path = root; }
+
///////////////////////////////////////////////////////////////////
//
// system architecture
*/
Pathname systemRoot() const;
+ /** The RepoManager root directory.
+ * Returns the same as \sa systemRoot() if not explicitely set.
+ */
+ Pathname repoManagerRoot() const;
+
+ /** Sets the RepoManager root directory.
+ * \sa repoManagerRoot()
+ */
+ void setRepoManagerRoot ( const Pathname &root );
+
public:
/** The autodetected system architecture. */
--- /dev/null
+/*---------------------------------------------------------------------\
+| ____ _ __ __ ___ |
+| |__ / \ / / . \ . \ |
+| / / \ V /| _/ _/ |
+| / /__ | | | | | | |
+| /_____||_| |_| |_| |
+| |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/DrunkenBishop.cc
+ */
+#include <iostream>
+//#include "zypp/base/LogTools.h"
+#include "zypp/base/Flags.h"
+#include "zypp/base/String.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/DrunkenBishop.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{
+ ///////////////////////////////////////////////////////////////////
+ namespace base
+ {
+ ///////////////////////////////////////////////////////////////////
+ namespace
+ {
+ /** Direction the drunken Bishop wants to move. */
+ enum class Direction : std::uint8_t // actually 2 bits
+ {
+ NW = 0x0,
+ NE = 0x1,
+ SW = 0x2,
+ SE = 0x3,
+ };
+
+ /** Convert a hex digit (case insensitive) into it's (4bit) integral value.
+ * \throws std::invalid_argument if char
+ */
+ inline std::uint8_t hexDigit( char ch_r )
+ {
+ switch ( ch_r )
+ {
+ case 'F': case 'f': return 15;
+ case 'E': case 'e': return 14;
+ case 'D': case 'd': return 13;
+ case 'C': case 'c': return 12;
+ case 'B': case 'b': return 11;
+ case 'A': case 'a': return 10;
+ case '9': return 9;
+ case '8': return 8;
+ case '7': return 7;
+ case '6': return 6;
+ case '5': return 5;
+ case '4': return 4;
+ case '3': return 3;
+ case '2': return 2;
+ case '1': return 1;
+ case '0': return 0;
+ }
+ throw std::invalid_argument( str::Str() << "Not a hex digit '" << ch_r << "'" );
+ }
+ } // namespace
+ ///////////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////////
+ /// \class DrunkenBishop::Impl
+ /// \brief DrunkenBishop implementation.
+ ///////////////////////////////////////////////////////////////////
+ class DrunkenBishop::Impl : private base::NonCopyable
+ {
+ public:
+ /** Default is an empty board. */
+ Impl()
+ : _h( 0U )
+ , _w( 0u )
+ , _s( 0U )
+ , _e( 0U )
+ , _renderSSH( true )
+ {}
+
+ /** Build up a new board.
+ * \throws std::invalid_argument
+ */
+ void compute( const std::string & data_r, const std::string & title_r, unsigned height_r = Auto, unsigned width_r = Auto )
+ {
+ // store rendering details
+ _renderSSH = ( data_r.size() <= 32 ); // up to the ssh fingerprint size
+ _fp = str::toUpper( data_r.size() <= 8 ? data_r : data_r.substr( data_r.size()-8 ) );
+ _tt = title_r;
+
+ // init the board
+ _h = odd(height_r);
+ _w = odd(width_r);
+
+ if ( _h == Auto )
+ {
+ if ( _renderSSH )
+ { _w = 17; _h = 9; }
+ else
+ { _w = 19; _h = 11; }
+ }
+ else if ( _w == Auto )
+ {
+ _w = (2*_h)-1;
+ }
+
+ _board = std::vector<std::uint8_t>( _w*_h, 0 );
+ _s = _w*_h/2; // start
+ _e = _s; // current/end
+ ++_board[_e];
+
+ // go
+ for ( const char * ch = data_r.c_str(); *ch; /*NOOP*/ )
+ {
+ std::uint8_t next4 = bite( ch );
+ // next4: 0x94
+ // bits: 10 01 01 00
+ // step: 4 3 2 1
+ static const std::uint8_t stepMask(0x3);
+ move( Direction( next4 & stepMask ) );
+ move( Direction( (next4>>2) & stepMask ) );
+ move( Direction( (next4>>4) & stepMask ) );
+ move( Direction( (next4>>6) ) );
+ }
+ }
+
+ /** Render board to a stream. */
+ std::ostream & dumpOn( std::ostream & str, const std::string & prefix_r, Options options_r ) const
+ {
+ if ( _board.empty() )
+ {
+ // "++\n"
+ // "++"
+ return str << prefix_r << "++" << endl << prefix_r << "++";
+ }
+
+ static const char * colorReset = "\033[0m";
+ static const char * colorBg = "\033[48;5;242m";
+ bool useColor = options_r.testFlag( USE_COLOR );
+
+ renderTitleOn( str << prefix_r , _tt );
+
+ for ( unsigned p = 0; p < _board.size(); ++p )
+ {
+ if ( ( p % _w ) == 0 )
+ {
+ if ( p )
+ str << ( useColor ? colorReset: "" ) << '|';
+ str << endl << prefix_r << '|' << ( useColor ? colorBg : "" );
+ }
+ renderOn( str, useColor, p );
+ }
+ str << ( useColor ? colorReset: "" ) << '|';
+
+ renderTitleOn( str << endl << prefix_r, _fp );
+ return str;
+ }
+
+ private:
+ /** Increment even width/height values. */
+ static unsigned odd( unsigned val_r )
+ { return( val_r == Auto ? val_r : val_r|1U ); }
+
+ /** Get next 4 moves (8 bit) from next 2 hex digits (1st digit != '\0' asserted, 0-pad if necessary).
+ * \throws std::invalid_argument if char is not a hex digit or 1st char is \c \0
+ */
+ static std::uint8_t bite( const char *& ch_r )
+ {
+ std::uint8_t ret = hexDigit( *ch_r ) << 4;
+ if ( *(++ch_r) )
+ ret |= hexDigit( *(ch_r++) );
+ return ret;
+ }
+
+ private:
+ /** Move Bishop from \ref _e into \a direction_r and update the \ref _board. */
+ void move( Direction direction_r )
+ {
+ switch ( direction_r )
+ {
+ case Direction::NW:
+ if ( atTL() )
+ /*no move*/;
+ else if ( atT() )
+ _e -= 1;
+ else if ( atL() )
+ _e -= _w;
+ else
+ _e -= _w+1;
+ break;
+
+ case Direction::NE:
+ if ( atTR() )
+ /*no move*/;
+ else if ( atT() )
+ _e += 1;
+ else if ( atR() )
+ _e -= _w;
+ else
+ _e -= _w-1;
+ break;
+
+ case Direction::SW:
+ if ( atBL() )
+ /*no move*/;
+ else if ( atB() )
+ _e -= 1;
+ else if ( atL() )
+ _e += _w;
+ else
+ _e += _w-1;
+ break;
+
+ case Direction::SE:
+ if ( atBR() )
+ /*no move*/;
+ else if ( atB() )
+ _e += 1;
+ else if ( atR() )
+ _e += _w;
+ else
+ _e += _w+1;
+ break;
+
+ default:
+ throw std::invalid_argument( str::Str() << "Bad Direction " << unsigned(direction_r) );
+ }
+ // update the board
+ ++_board[_e];
+ }
+
+ /** Whether \ref _e is in the top left corner. */
+ bool atTL() const
+ { return( _e == 0 ); }
+
+ /** Whether \ref _e is in the top right corner. */
+ bool atTR() const
+ { return( _e == _w-1 ); }
+
+ /** Whether \ref _e is in the bottom left corner. */
+ bool atBL() const
+ { return( _e == _board.size()-_w ); }
+
+ /** Whether \ref _e is in the bottom right corner. */
+ bool atBR() const
+ { return( _e == _board.size()-1 ); }
+
+ /** Whether \ref _e is in the top row. */
+ bool atT() const
+ { return( _e < _w ); }
+
+ /** Whether \ref _e is in the bottom row. */
+ bool atB() const
+ { return( _e >= _board.size()-_w ); }
+
+ /** Whether \ref _e is in the left column. */
+ bool atL() const
+ { return( ( _e % _w ) == 0 ); }
+
+ /** Whether \ref _e is in the right column. */
+ bool atR() const
+ { return( ( _e % _w ) == (_w-1) ); }
+
+ private:
+ /** ANSI color heatmap. */
+ const char * color( std::uint8_t idx_r ) const
+ {
+ static const std::vector<const char *> colors = {
+ "", // no coin
+ "\033[38;5;21m", // blue (cold)
+ "\033[38;5;39m",
+ "\033[38;5;50m",
+ "\033[38;5;48m",
+ "\033[38;5;46m", // green
+ "\033[38;5;118m",
+ "\033[38;5;190m",
+ "\033[38;5;226m", // yellow
+ "\033[38;5;220m",
+ "\033[38;5;214m", // orange
+ "\033[38;5;208m",
+ "\033[38;5;202m",
+ "\033[38;5;196m", // red
+ "\033[38;5;203m",
+ "\033[38;5;210m",
+ "\033[38;5;217m", // pink
+ "\033[38;5;224m",
+ "\033[38;5;231m", // white (hot)
+ };
+#if 0
+ // cycle through heat map to test all colors
+ if ( ! idx_r )
+ return "";
+ static unsigned i = 0;
+ if ( ++i == colors.size() )
+ i = 1;
+ return colors[i];
+#endif
+ return ( idx_r < colors.size() ? colors[idx_r] : *colors.rbegin() );
+ }
+
+ /** Render non empty title strings */
+ std::ostream & renderTitleOn( std::ostream & str, const std::string & title_r ) const
+ {
+ std::string buffer( _w+2, '-' );
+ *buffer.begin() = *buffer.rbegin() = '+';
+
+ if ( !title_r.empty() && _w >= 2 ) // extra 2 for "[]"
+ {
+ std::string::size_type tlen = std::min( title_r.size(), std::string::size_type(_w-2) );
+ std::string::size_type tpos = (_w-tlen)/2; // not (_w-2-tlen) because buffer is size _w+2
+ buffer[tpos++] = '[';
+ for ( std::string::size_type p = 0; p < tlen; ++p, ++tpos )
+ buffer[tpos] = title_r[p];
+ buffer[tpos] = ']';
+ }
+ return str << buffer;
+ }
+
+ /** Render board numbers to printable chars. */
+ std::ostream & renderOn( std::ostream & str, bool useColor_r, unsigned pos_r ) const
+ {
+ static const std::string sshSet( " .o+=*BOX@%&#/^" );
+ static const std::string gpgSet( " .^:li?(fxXZ#MW&8%@" );
+ const std::string & charSet( _renderSSH ? sshSet : gpgSet );
+
+ if ( useColor_r )
+ str << color( _board[pos_r] );
+
+ if ( pos_r == _e )
+ return str << 'E';
+
+ if ( pos_r == _s )
+ return str << 'S';
+
+ return str << ( _board[pos_r] < charSet.size() ? charSet[_board[pos_r]] : *charSet.rbegin() );
+ }
+
+ private:
+ /** Request default width/height values. */
+ static constexpr const unsigned Auto = unsigned(-1);
+
+ private:
+ std::vector<std::uint8_t> _board; ///< the board
+ unsigned _h; ///< board height
+ unsigned _w; ///< board with
+ unsigned _s; ///< start position
+ unsigned _e; ///< end position
+
+ private:
+ bool _renderSSH; ///< whether to render the ssh (or gpg) char set
+ std::string _fp; ///< fingerprint to render as bottom title
+ std::string _tt; ///< text to render as top title
+
+ public:
+ /** Offer default Impl. */
+ static shared_ptr<Impl> nullimpl()
+ {
+ static shared_ptr<Impl> _nullimpl( new Impl );
+ return _nullimpl;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////
+ // CLASS NAME : DrunkenBishop
+ ///////////////////////////////////////////////////////////////////
+
+ DrunkenBishop::DrunkenBishop()
+ : _pimpl( Impl::nullimpl() )
+ { /*nothing to compute*/ }
+
+ DrunkenBishop::DrunkenBishop( const std::string & data_r, const std::string & title_r )
+ : _pimpl( new Impl )
+ { _pimpl->compute( data_r, title_r ); }
+
+ DrunkenBishop::DrunkenBishop( const std::string & data_r, const std::string & title_r, unsigned height_r )
+ : _pimpl( new Impl )
+ { _pimpl->compute( data_r, title_r, height_r ); }
+
+ DrunkenBishop::DrunkenBishop( const std::string & data_r, const std::string & title_r, unsigned height_r, unsigned width_r )
+ : _pimpl( new Impl )
+ { _pimpl->compute( data_r, title_r, height_r, width_r ); }
+
+ DrunkenBishop::~DrunkenBishop()
+ {}
+
+ std::ostream & DrunkenBishop::dumpOn( std::ostream & str, const std::string & prefix_r, Options options_r ) const
+ { return _pimpl->dumpOn( str, prefix_r, options_r ); }
+
+ std::string DrunkenBishop::asString( const std::string & prefix_r, Options options_r ) const
+ {
+ std::ostringstream str;
+ dumpOn( str, prefix_r, options_r );
+ return str.str();
+ }
+
+ std::vector<std::string> DrunkenBishop::asLines( const std::string & prefix_r, Options options_r ) const
+ {
+ std::vector<std::string> ret;
+ str::split( asString( prefix_r, options_r ), std::back_inserter(ret), "\n" );
+ return ret;
+ }
+
+ } // namespace base
+ ///////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
--- /dev/null
+/*---------------------------------------------------------------------\
+| ____ _ __ __ ___ |
+| |__ / \ / / . \ . \ |
+| / / \ V /| _/ _/ |
+| / /__ | | | | | | |
+| /_____||_| |_| |_| |
+| |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/DrunkenBishop.h
+ */
+#ifndef ZYPP_BASE_DRUNKENBISHOP_H
+#define ZYPP_BASE_DRUNKENBISHOP_H
+
+#include <iosfwd>
+#include <vector>
+#include <string>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Flags.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{
+ ///////////////////////////////////////////////////////////////////
+ namespace base
+ {
+ ///////////////////////////////////////////////////////////////////
+ /// \class DrunkenBishop
+ /// \brief Random art fingerprint visualization
+ /// Visualize fingerprint data on a [17x9] (SSH) or [19x11] (GPG) or
+ /// custom sized board. The default board size and layout depends on
+ /// the data string length (above 32 the GPG board and layout is used).
+ ///
+ /// The data string should be an even sized HEX string, otherwise
+ /// it will be 0-padded.
+ ///
+ /// All ctor calls may throw \ref std::invalid_argument.
+ ///
+ /// \code
+ /// [SuSE Package Signing Key <build@suse.de>]
+ /// +------[Title]------+
+ /// | . . ^ | fpr FEAB502539D846DB2C0961CA70AF9E8139DB7C82
+ /// | : : . | id 70AF9E8139DB7C82
+ /// | ^ ^ . | cre 1481108255 Wed Dec 7 11:57:35 2016
+ /// | ^ . l i | exp 1607252255 Sun Dec 6 11:57:35 2020
+ /// | : ^ . f : | ttl 992
+ /// | ? ^ .Sl | rpm 39db7c82-5847eb1f
+ /// | E i ... |
+ /// | ^ .. |
+ /// | . . |
+ /// | . . |
+ /// | .... |
+ /// +----[39DB7C82]-----+
+ /// \endcode
+ ////
+ /// Based on https://github.com/atoponce/keyart, the development location
+ /// for the Debian signing-party package. We try to use the same charset
+ /// and heatmap.
+ /// See also http://dirk-loss.de/sshvis/drunken_bishop.pdf.
+ ///////////////////////////////////////////////////////////////////
+ class DrunkenBishop
+ {
+ friend std::ostream & operator<<( std::ostream & str, const DrunkenBishop & obj );
+
+ public:
+ /** Default ctor: empty board (1x1) */
+ DrunkenBishop();
+
+ /** Ctor taking a data string (and optional title) and using a default (SSH/GPG) board. */
+ DrunkenBishop( const std::string & data_r, const std::string & title_r = std::string() );
+
+ /** Ctor also taking a desired board height (even value is incremented, width is 2*height-1). */
+ DrunkenBishop( const std::string & data_r, const std::string & title_r, unsigned height_r );
+ /** Ctor \overload without optional title */
+ DrunkenBishop( const std::string & data_r, unsigned height_r )
+ : DrunkenBishop( data_r, std::string(), height_r )
+ {}
+
+ /** Ctor also taking a desired board height and width (even values are incremented). */
+ DrunkenBishop( const std::string & data_r, const std::string & title_r, unsigned height_r, unsigned width_r );
+ /** Ctor \overload without optional title */
+ DrunkenBishop( const std::string & data_r, unsigned height_r, unsigned width_r )
+ : DrunkenBishop( data_r, std::string(), height_r, width_r )
+ {}
+
+ /** Dtor */
+ ~DrunkenBishop();
+
+ public:
+ /* Rendering options */
+ enum OptionBits {
+ USE_COLOR = (1<<0), ///< use colors
+ };
+ ZYPP_DECLARE_FLAGS(Options,OptionBits);
+
+ /** Render board to steam.*/
+ std::ostream & dumpOn( std::ostream & str, Options options_r = Options() ) const
+ { return dumpOn( str, std::string(), options_r ); }
+ /** \overload taking an indent string prefixing each line. */
+ std::ostream & dumpOn( std::ostream & str, const std::string & prefix_r, Options options_r = Options() ) const;
+
+ /** Render board as string.*/
+ std::string asString( Options options_r = Options() ) const
+ { return asString( std::string(), options_r ); }
+ /** \overload taking an indent string prefixing each line. */
+ std::string asString( const std::string & prefix_r, Options options_r = Options() ) const;
+
+ /** Render to an array of lines. */
+ std::vector<std::string> asLines( Options options_r = Options() ) const
+ { return asLines( std::string(), options_r ); }
+ /** \overload taking an indent string prefixing each line. */
+ std::vector<std::string> asLines( const std::string & prefix_r, Options options_r = Options() ) const;
+
+ public:
+ class Impl; ///< Implementation class.
+ private:
+ RW_pointer<Impl> _pimpl; ///< Pointer to implementation.
+ };
+
+ ZYPP_DECLARE_OPERATORS_FOR_FLAGS(DrunkenBishop::Options);
+
+ /** \relates DrunkenBishop Stream output */
+ inline std::ostream & operator<<( std::ostream & str, const DrunkenBishop & obj )
+ { return obj.dumpOn( str ); }
+
+ } // namespace base
+ ///////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_DRUNKENBISHOP_H
std::string mountpoint( attachPoint().asString() );
Mount mount;
- CredentialManager cm;
+ CredentialManager cm(CredManagerOptions(ZConfig::instance().repoManagerRoot()));
Mount::Options options( _url.getQueryParam("mountoptions") );
string username = _url.getUsername();
bool MediaCIFS::authenticate(AuthData & authdata, bool firstTry) const
{
//! \todo need a way to pass different CredManagerOptions here
- CredentialManager cm(CredManagerOptions(ZConfig::instance().systemRoot()));
+ CredentialManager cm(CredManagerOptions(ZConfig::instance().repoManagerRoot()));
// get stored credentials
AuthData_Ptr cmcred = cm.getCred(_url);
bool MediaCurl::authenticate(const string & availAuthTypes, bool firstTry) const
{
//! \todo need a way to pass different CredManagerOptions here
- Target_Ptr target = zypp::getZYpp()->getTarget();
- CredentialManager cm(CredManagerOptions(target ? target->root() : ""));
+ CredentialManager cm(CredManagerOptions(ZConfig::instance().repoManagerRoot()));
CurlAuthData_Ptr credentials;
// get stored credentials
if ( empty() ) // at init / after reset
{
// load user definitions from vars.d
- filesystem::dirForEach( ZConfig::instance().systemRoot() / ZConfig::instance().varsPath(),
+ filesystem::dirForEach( ZConfig::instance().repoManagerRoot() / ZConfig::instance().varsPath(),
filesystem::matchNoDots(), bind( &RepoVarsMap::parse, this, _1, _2 ) );
// releasever_major/_minor are per default derived from releasever.
// If releasever is userdefined, inject missing _major/_minor too.
struct RIMServiceRepos : public ServiceRepos::Impl
{
- RIMServiceRepos( const ServiceInfo & service,
+ RIMServiceRepos( const Pathname & /*root_r*/,
+ const ServiceInfo & service,
const ServiceRepos::ProcessRepo & callback,
const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() )
{
struct PluginServiceRepos : public ServiceRepos::Impl
{
- PluginServiceRepos( const ServiceInfo & service,
+ PluginServiceRepos( const Pathname & root_r,
+ const ServiceInfo & service,
const ServiceRepos::ProcessRepo & callback,
const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() )
{
- Url serviceUrl( service.url() );
+ // bsc#1080693: Service script needs to be executed chrooted to the RepoManagers rootDir.
+ // The service is not aware of the rootDir, so it's explicitly passed and needs to be
+ // stripped from the URLs path.
stringstream buffer;
ExternalProgram::Arguments args;
args.reserve( 3 );
args.push_back( "/bin/sh" );
args.push_back( "-c" );
- args.push_back( serviceUrl.getPathName() );
- ExternalProgramWithStderr prog( args );
+ args.push_back( Pathname::stripprefix( root_r, service.url().getPathName() ).asString() );
+ ExternalProgramWithStderr prog( args, root_r );
prog >> buffer;
if ( prog.close() != 0 )
///////////////////////////////////////////////////////////////////
- ServiceRepos::ServiceRepos( const ServiceInfo & service,
+ ServiceRepos::ServiceRepos( const Pathname & root_r,
+ const ServiceInfo & service,
const ServiceRepos::ProcessRepo & callback,
const ProgressData::ReceiverFnc &progress )
: _impl( ( service.type() == ServiceType::PLUGIN )
- ? static_cast<ServiceRepos::Impl*>( new PluginServiceRepos( service, callback, progress ) )
- : static_cast<ServiceRepos::Impl*>( new RIMServiceRepos (service, callback, progress ) ) )
+ ? static_cast<ServiceRepos::Impl*>( new PluginServiceRepos( root_r, service, callback, progress ) )
+ : static_cast<ServiceRepos::Impl*>( new RIMServiceRepos( root_r, service, callback, progress ) ) )
{}
ServiceRepos::~ServiceRepos()
public:
/**
* Return false from the callback to get a \ref AbortRequestException
- * to be thrown and the processing to be cancelled.
+ * to be thrown and the processing to be canceled.
*/
typedef function< bool( const RepoInfo & )> ProcessRepo;
- ServiceRepos( const ServiceInfo & service,
+ /**
+ * bsc#1080693: Explicitly pass the RemoManagers rootDir until it can be queried from the ServiceInfo.
+ * Required to execute plugin services chrooted.
+ */
+ ServiceRepos( const Pathname & root_r,
+ const ServiceInfo & service,
const ProcessRepo & callback,
const ProgressData::ReceiverFnc &progress = ProgressData::ReceiverFnc() );
~ServiceRepos();
#include <fstream>
#include "zypp/base/LogTools.h"
#include "zypp/base/NonCopyable.h"
+#include "zypp/base/Gettext.h"
#include "zypp/target/RpmPostTransCollector.h"
#include "zypp/TmpPath.h"
#include "zypp/ExternalProgram.h"
#include "zypp/target/rpm/RpmHeader.h"
#include "zypp/ZConfig.h"
+#include "zypp/ZYppCallbacks.h"
using std::endl;
#undef ZYPP_BASE_LOGGER_LOGGROUP
return true;
}
- /** Execute te remembered scripts. */
- void executeScripts()
+ /** Execute the remembered scripts. */
+ bool executeScripts()
{
if ( _scripts.empty() )
- return;
+ return true;
HistoryLog historylog;
Pathname noRootScriptDir( ZConfig::instance().update_scriptsPath() / tmpDir().basename() );
- for ( const auto & script : _scripts )
+ ProgressData scriptProgress( static_cast<ProgressData::value_type>(_scripts.size()) );
+ callback::SendReport<ProgressReport> report;
+ scriptProgress.sendTo( ProgressReportAdaptor( ProgressData::ReceiverFnc(), report ) );
+
+ bool firstScript = true;
+ while ( ! _scripts.empty() )
{
+ const std::string & script = _scripts.front();
+ const std::string & pkgident( script.substr( 0, script.size()-6 ) ); // strip tmp file suffix
+
+ scriptProgress.name( str::Format(_("Executing %%posttrans script '%1%'")) % pkgident );
+
+ bool canContinue = true;
+ if (firstScript) {
+ firstScript = false;
+ canContinue = scriptProgress.toMin();
+ } else {
+ canContinue = scriptProgress.incr();
+ }
+
+ if (!canContinue) {
+ str::Str msg;
+ msg << "Execution of %posttrans scripts cancelled";
+ WAR << msg << endl;
+ historylog.comment( msg, true /*timestamp*/);
+ JobReport::warning( msg );
+ return false;
+ }
+
MIL << "EXECUTE posttrans: " << script << endl;
- ExternalProgram prog( (noRootScriptDir/script).asString(), ExternalProgram::Stderr_To_Stdout, false, -1, true, _root );
+ ExternalProgram prog( (noRootScriptDir/script).asString(), ExternalProgram::Stderr_To_Stdout, false, -1, true, _root );
str::Str collect;
for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
DBG << line;
collect << " " << line;
}
+
+ //script was executed, remove it from the list
+ _scripts.pop_front();
+
int ret = prog.close();
const std::string & scriptmsg( collect );
if ( ret != 0 || ! scriptmsg.empty() )
{
- const std::string & pkgident( script.substr( 0, script.size()-6 ) ); // strip tmp file suffix
-
if ( ! scriptmsg.empty() )
{
str::Str msg;
}
}
}
+
+ //show a final message
+ scriptProgress.name( _("Executing %posttrans scripts") );
+ scriptProgress.toMax();
_scripts.clear();
+ return true;
}
/** Discard all remembered scrips. */
_scripts.clear();
}
+
private:
/** Lazy create tmpdir on demand. */
Pathname tmpDir()
bool RpmPostTransCollector::collectScriptFromPackage( ManagedFile rpmPackage_r )
{ return _pimpl->collectScriptFromPackage( rpmPackage_r ); }
- void RpmPostTransCollector::executeScripts()
+ bool RpmPostTransCollector::executeScripts()
{ return _pimpl->executeScripts(); }
void RpmPostTransCollector::discardScripts()
bool collectScriptFromPackage( ManagedFile rpmPackage_r );
/** Execute te remembered scripts. */
- void executeScripts();
+ bool executeScripts();
/** Discard all remembered scrips. */
void discardScripts();
// process all remembered posttrans scripts.
if ( !abort )
- postTransCollector.executeScripts();
- else
+ abort = postTransCollector.executeScripts();
+
+ if ( abort )
postTransCollector.discardScripts();
// Check presence of update scripts/messages. If aborting,