libzypp  17.3.1
RpmDb.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
12 #include "librpm.h"
13 extern "C"
14 {
15 #include <rpm/rpmcli.h>
16 #include <rpm/rpmlog.h>
17 }
18 #include <cstdlib>
19 #include <cstdio>
20 #include <ctime>
21 
22 #include <iostream>
23 #include <fstream>
24 #include <sstream>
25 #include <list>
26 #include <map>
27 #include <set>
28 #include <string>
29 #include <vector>
30 #include <algorithm>
31 
32 #include "zypp/base/Logger.h"
33 #include "zypp/base/String.h"
34 #include "zypp/base/Gettext.h"
35 #include "zypp/base/LocaleGuard.h"
36 
37 #include "zypp/Date.h"
38 #include "zypp/Pathname.h"
39 #include "zypp/PathInfo.h"
40 #include "zypp/PublicKey.h"
41 
42 #include "zypp/target/rpm/RpmDb.h"
44 
45 #include "zypp/HistoryLog.h"
48 #include "zypp/TmpPath.h"
49 #include "zypp/KeyRing.h"
50 #include "zypp/ZYppFactory.h"
51 #include "zypp/ZConfig.h"
52 
53 using std::endl;
54 using namespace zypp::filesystem;
55 
56 #define WARNINGMAILPATH "/var/log/YaST2/"
57 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
58 #define MAXRPMMESSAGELINES 10000
59 
60 #define WORKAROUNDRPMPWDBUG
61 
62 namespace zypp
63 {
64  namespace zypp_readonly_hack
65  {
66  bool IGotIt(); // in readonly-mode
67  }
68 namespace target
69 {
70 namespace rpm
71 {
72 namespace
73 {
74 #if 1 // No more need to escape whitespace since rpm-4.4.2.3
75 const char* quoteInFilename_m = "\'\"";
76 #else
77 const char* quoteInFilename_m = " \t\'\"";
78 #endif
79 inline std::string rpmQuoteFilename( const Pathname & path_r )
80 {
81  std::string path( path_r.asString() );
82  for ( std::string::size_type pos = path.find_first_of( quoteInFilename_m );
83  pos != std::string::npos;
84  pos = path.find_first_of( quoteInFilename_m, pos ) )
85  {
86  path.insert( pos, "\\" );
87  pos += 2; // skip '\\' and the quoted char.
88  }
89  return path;
90 }
91 
92 
97  inline Pathname workaroundRpmPwdBug( Pathname path_r )
98  {
99 #if defined(WORKAROUNDRPMPWDBUG)
100  if ( path_r.relative() )
101  {
102  // try to prepend cwd
103  AutoDispose<char*> cwd( ::get_current_dir_name(), ::free );
104  if ( cwd )
105  return Pathname( cwd ) / path_r;
106  WAR << "Can't get cwd!" << endl;
107  }
108 #endif
109  return path_r; // no problem with absolute pathnames
110  }
111 }
112 
114 {
115  KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
116  {
117  connect();
118  }
119 
121  {
122  disconnect();
123  }
124 
125  virtual void trustedKeyAdded( const PublicKey &key )
126  {
127  MIL << "trusted key added to zypp Keyring. Importing..." << endl;
128  _rpmdb.importPubkey( key );
129  }
130 
131  virtual void trustedKeyRemoved( const PublicKey &key )
132  {
133  MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
134  _rpmdb.removePubkey( key );
135  }
136 
138 };
139 
140 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
141 
142 unsigned diffFiles(const std::string file1, const std::string file2, std::string& out, int maxlines)
143 {
144  const char* argv[] =
145  {
146  "diff",
147  "-u",
148  file1.c_str(),
149  file2.c_str(),
150  NULL
151  };
152  ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
153 
154  //if(!prog)
155  //return 2;
156 
157  std::string line;
158  int count = 0;
159  for (line = prog.receiveLine(), count=0;
160  !line.empty();
161  line = prog.receiveLine(), count++ )
162  {
163  if (maxlines<0?true:count<maxlines)
164  out+=line;
165  }
166 
167  return prog.close();
168 }
169 
170 
171 
172 /******************************************************************
173  **
174  **
175  ** FUNCTION NAME : stringPath
176  ** FUNCTION TYPE : inline std::string
177 */
178 inline std::string stringPath( const Pathname & root_r, const Pathname & sub_r )
179 {
180  return librpmDb::stringPath( root_r, sub_r );
181 }
182 
183 /******************************************************************
184  **
185  **
186  ** FUNCTION NAME : operator<<
187  ** FUNCTION TYPE : std::ostream &
188 */
189 std::ostream & operator<<( std::ostream & str, const RpmDb::DbStateInfoBits & obj )
190 {
191  if ( obj == RpmDb::DbSI_NO_INIT )
192  {
193  str << "NO_INIT";
194  }
195  else
196  {
197 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
198  str << "V4(";
199  ENUM_OUT( DbSI_HAVE_V4, 'X' );
200  ENUM_OUT( DbSI_MADE_V4, 'c' );
201  ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
202  str << ")V3(";
203  ENUM_OUT( DbSI_HAVE_V3, 'X' );
204  ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
205  ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
206  str << ")";
207 #undef ENUM_OUT
208  }
209  return str;
210 }
211 
212 
213 
215 //
216 // CLASS NAME : RpmDb
217 //
219 
220 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
221 
223 
225 //
226 //
227 // METHOD NAME : RpmDb::RpmDb
228 // METHOD TYPE : Constructor
229 //
230 RpmDb::RpmDb()
231  : _dbStateInfo( DbSI_NO_INIT )
232 #warning Check for obsolete memebers
233  , _backuppath ("/var/adm/backup")
234  , _packagebackups(false)
235  , _warndirexists(false)
236 {
237  process = 0;
238  exit_code = -1;
240  // Some rpm versions are patched not to abort installation if
241  // symlink creation failed.
242  setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
243  sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
244 }
245 
247 //
248 //
249 // METHOD NAME : RpmDb::~RpmDb
250 // METHOD TYPE : Destructor
251 //
253 {
254  MIL << "~RpmDb()" << endl;
255  closeDatabase();
256  delete process;
257  MIL << "~RpmDb() end" << endl;
258  sKeyRingReceiver.reset();
259 }
260 
262 {
263  Date ts_rpm;
264 
265  Pathname db_path;
266  if ( dbPath().empty() )
267  db_path = "/var/lib/rpm";
268  else
269  db_path = dbPath();
270 
271  PathInfo rpmdb_info(root() + db_path + "/Packages");
272 
273  if ( rpmdb_info.isExist() )
274  return rpmdb_info.mtime();
275  else
276  return Date::now();
277 }
279 //
280 //
281 // METHOD NAME : RpmDb::dumpOn
282 // METHOD TYPE : std::ostream &
283 //
284 std::ostream & RpmDb::dumpOn( std::ostream & str ) const
285 {
286  str << "RpmDb[";
287 
288  if ( _dbStateInfo == DbSI_NO_INIT )
289  {
290  str << "NO_INIT";
291  }
292  else
293  {
294 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
295  str << "V4(";
296  ENUM_OUT( DbSI_HAVE_V4, 'X' );
297  ENUM_OUT( DbSI_MADE_V4, 'c' );
298  ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
299  str << ")V3(";
300  ENUM_OUT( DbSI_HAVE_V3, 'X' );
301  ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
302  ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
303  str << "): " << stringPath( _root, _dbPath );
304 #undef ENUM_OUT
305  }
306  return str << "]";
307 }
308 
310 //
311 //
312 // METHOD NAME : RpmDb::initDatabase
313 // METHOD TYPE : PMError
314 //
315 void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
316 {
318  // Check arguments
320  bool quickinit( root_r.empty() );
321 
322  if ( root_r.empty() )
323  root_r = "/";
324 
325  if ( dbPath_r.empty() )
326  dbPath_r = "/var/lib/rpm";
327 
328  if ( ! (root_r.absolute() && dbPath_r.absolute()) )
329  {
330  ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
331  ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
332  }
333 
334  MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
335  << ( doRebuild_r ? " (rebuilddb)" : "" )
336  << ( quickinit ? " (quickinit)" : "" ) << endl;
337 
339  // Check whether already initialized
341  if ( initialized() )
342  {
343  if ( root_r == _root && dbPath_r == _dbPath )
344  {
345  return;
346  }
347  else
348  {
349  ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
350  }
351  }
352 
354  // init database
357 
358  if ( quickinit )
359  {
360  MIL << "QUICK initDatabase (no systemRoot set)" << endl;
361  return;
362  }
363 
365  try
366  {
367  internal_initDatabase( root_r, dbPath_r, info );
368  }
369  catch (const RpmException & excpt_r)
370  {
371  ZYPP_CAUGHT(excpt_r);
373  ERR << "Cleanup on error: state " << info << endl;
374 
375  if ( dbsi_has( info, DbSI_MADE_V4 ) )
376  {
377  // remove the newly created rpm4 database and
378  // any backup created on conversion.
379  removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
380  }
381  ZYPP_RETHROW(excpt_r);
382  }
383  if ( dbsi_has( info, DbSI_HAVE_V3 ) )
384  {
385  if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
386  {
387  // Move obsolete rpm3 database beside.
388  MIL << "Cleanup: state " << info << endl;
389  removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
390  dbsi_clr( info, DbSI_HAVE_V3 );
391  }
392  else
393  {
394  // Performing an update: Keep the original rpm3 database
395  // and wait if the rpm4 database gets modified by installing
396  // or removing packages. Cleanup in modifyDatabase or closeDatabase.
397  MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
398  }
399  }
400 #warning CHECK: notify root about conversion backup.
401 
402  _root = root_r;
403  _dbPath = dbPath_r;
404  _dbStateInfo = info;
405 
406  if ( doRebuild_r )
407  {
408  if ( dbsi_has( info, DbSI_HAVE_V4 )
409  && ! dbsi_has( info, DbSI_MADE_V4 ) )
410  {
411  rebuildDatabase();
412  }
413  }
414 
415  MIL << "Synchronizing keys with zypp keyring" << endl;
416  syncTrustedKeys();
417 
418  // Close the database in case any write acces (create/convert)
419  // happened during init. This should drop any lock acquired
420  // by librpm. On demand it will be reopened readonly and should
421  // not hold any lock.
422  librpmDb::dbRelease( true );
423 
424  MIL << "InitDatabase: " << *this << endl;
425 }
426 
428 //
429 //
430 // METHOD NAME : RpmDb::internal_initDatabase
431 // METHOD TYPE : PMError
432 //
433 void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
434  DbStateInfoBits & info_r )
435 {
436  info_r = DbSI_NO_INIT;
437 
439  // Get info about the desired database dir
441  librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
442 
443  if ( dbInfo.illegalArgs() )
444  {
445  // should not happen (checked in initDatabase)
446  ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
447  }
448  if ( ! dbInfo.usableArgs() )
449  {
450  ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
451  ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
452  }
453 
454  if ( dbInfo.hasDbV4() )
455  {
456  dbsi_set( info_r, DbSI_HAVE_V4 );
457  MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
458  }
459  else
460  {
461  MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
462  }
463 
464  if ( dbInfo.hasDbV3() )
465  {
466  dbsi_set( info_r, DbSI_HAVE_V3 );
467  }
468  if ( dbInfo.hasDbV3ToV4() )
469  {
470  dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
471  }
472 
473  DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
474  librpmDb::dumpState( DBG ) << endl;
475 
477  // Access database, create if needed
479 
480  // creates dbdir and empty rpm4 database if not present
481  librpmDb::dbAccess( root_r, dbPath_r );
482 
483  if ( ! dbInfo.hasDbV4() )
484  {
485  dbInfo.restat();
486  if ( dbInfo.hasDbV4() )
487  {
488  dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
489  }
490  }
491 
492  DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
493  librpmDb::dumpState( DBG ) << endl;
494 
496  // Check whether to convert something. Create backup but do
497  // not remove anything here
499  librpmDb::constPtr dbptr;
500  librpmDb::dbAccess( dbptr );
501  bool dbEmpty = dbptr->empty();
502  if ( dbEmpty )
503  {
504  MIL << "Empty rpm4 database " << dbInfo.dbV4() << endl;
505  }
506 
507  if ( dbInfo.hasDbV3() )
508  {
509  MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
510 
511  if ( dbEmpty )
512  {
513  extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
514  convertV3toV4( dbInfo.dbV3().path(), dbptr );
515 
516  // create a backup copy
517  int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
518  if ( res )
519  {
520  WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
521  }
522  else
523  {
524  dbInfo.restat();
525  if ( dbInfo.hasDbV3ToV4() )
526  {
527  MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
529  }
530  }
531 
532  }
533  else
534  {
535 
536  WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
537  // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
538  dbsi_set( info_r, DbSI_MODIFIED_V4 );
539 
540  }
541 
542  DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
543  librpmDb::dumpState( DBG ) << endl;
544  }
545 
546  if ( dbInfo.hasDbV3ToV4() )
547  {
548  MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
549  }
550 }
551 
553 //
554 //
555 // METHOD NAME : RpmDb::removeV4
556 // METHOD TYPE : void
557 //
558 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
559 {
560  const char * v3backup = "packages.rpm3";
561  const char * master = "Packages";
562  const char * index[] =
563  {
564  "Basenames",
565  "Conflictname",
566  "Depends",
567  "Dirnames",
568  "Filemd5s",
569  "Group",
570  "Installtid",
571  "Name",
572  "Providename",
573  "Provideversion",
574  "Pubkeys",
575  "Requirename",
576  "Requireversion",
577  "Sha1header",
578  "Sigmd5",
579  "Triggername",
580  // last entry!
581  NULL
582  };
583 
584  PathInfo pi( dbdir_r );
585  if ( ! pi.isDir() )
586  {
587  ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
588  return;
589  }
590 
591  for ( const char ** f = index; *f; ++f )
592  {
593  pi( dbdir_r + *f );
594  if ( pi.isFile() )
595  {
596  filesystem::unlink( pi.path() );
597  }
598  }
599 
600  pi( dbdir_r + master );
601  if ( pi.isFile() )
602  {
603  MIL << "Removing rpm4 database " << pi << endl;
604  filesystem::unlink( pi.path() );
605  }
606 
607  if ( v3backup_r )
608  {
609  pi( dbdir_r + v3backup );
610  if ( pi.isFile() )
611  {
612  MIL << "Removing converted rpm3 database backup " << pi << endl;
613  filesystem::unlink( pi.path() );
614  }
615  }
616 }
617 
619 //
620 //
621 // METHOD NAME : RpmDb::removeV3
622 // METHOD TYPE : void
623 //
624 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
625 {
626  const char * master = "packages.rpm";
627  const char * index[] =
628  {
629  "conflictsindex.rpm",
630  "fileindex.rpm",
631  "groupindex.rpm",
632  "nameindex.rpm",
633  "providesindex.rpm",
634  "requiredby.rpm",
635  "triggerindex.rpm",
636  // last entry!
637  NULL
638  };
639 
640  PathInfo pi( dbdir_r );
641  if ( ! pi.isDir() )
642  {
643  ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
644  return;
645  }
646 
647  for ( const char ** f = index; *f; ++f )
648  {
649  pi( dbdir_r + *f );
650  if ( pi.isFile() )
651  {
652  filesystem::unlink( pi.path() );
653  }
654  }
655 
656 #warning CHECK: compare vs existing v3 backup. notify root
657  pi( dbdir_r + master );
658  if ( pi.isFile() )
659  {
660  Pathname m( pi.path() );
661  if ( v3backup_r )
662  {
663  // backup was already created
664  filesystem::unlink( m );
665  Pathname b( m.extend( "3" ) );
666  pi( b ); // stat backup
667  }
668  else
669  {
670  Pathname b( m.extend( ".deleted" ) );
671  pi( b );
672  if ( pi.isFile() )
673  {
674  // rempve existing backup
675  filesystem::unlink( b );
676  }
677  filesystem::rename( m, b );
678  pi( b ); // stat backup
679  }
680  MIL << "(Re)moved rpm3 database to " << pi << endl;
681  }
682 }
683 
685 //
686 //
687 // METHOD NAME : RpmDb::modifyDatabase
688 // METHOD TYPE : void
689 //
691 {
692  if ( ! initialized() )
693  return;
694 
695  // tag database as modified
697 
698  // Move outdated rpm3 database beside.
700  {
701  MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
704  }
705 }
706 
708 //
709 //
710 // METHOD NAME : RpmDb::closeDatabase
711 // METHOD TYPE : PMError
712 //
714 {
715  if ( ! initialized() )
716  {
717  return;
718  }
719 
720  MIL << "Calling closeDatabase: " << *this << endl;
721 
723  // Block further database access
726 
728  // Check fate if old version database still present
731  {
732  MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
734  {
735  // Move outdated rpm3 database beside.
737  }
738  else
739  {
740  // Remove unmodified rpm4 database
742  }
743  }
744 
746  // Uninit
748  _root = _dbPath = Pathname();
750 
751  MIL << "closeDatabase: " << *this << endl;
752 }
753 
755 //
756 //
757 // METHOD NAME : RpmDb::rebuildDatabase
758 // METHOD TYPE : PMError
759 //
761 {
763 
764  report->start( root() + dbPath() );
765 
766  try
767  {
769  }
770  catch (RpmException & excpt_r)
771  {
772  report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
773  ZYPP_RETHROW(excpt_r);
774  }
775  report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
776 }
777 
779 {
781 
782  MIL << "RpmDb::rebuildDatabase" << *this << endl;
783  // FIXME Timecount _t( "RpmDb::rebuildDatabase" );
784 
785  PathInfo dbMaster( root() + dbPath() + "Packages" );
786  PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
787 
788  // run rpm
789  RpmArgVec opts;
790  opts.push_back("--rebuilddb");
791  opts.push_back("-vv");
792 
793  // don't call modifyDatabase because it would remove the old
794  // rpm3 database, if the current database is a temporary one.
796 
797  // progress report: watch this file growing
798  PathInfo newMaster( root()
799  + dbPath().extend( str::form( "rebuilddb.%d",
800  process?process->getpid():0) )
801  + "Packages" );
802 
803  std::string line;
804  std::string errmsg;
805 
806  while ( systemReadLine( line ) )
807  {
808  if ( newMaster() )
809  { // file is removed at the end of rebuild.
810  // current size should be upper limit for new db
811  if ( ! report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath()) )
812  {
813  WAR << "User requested abort." << endl;
814  systemKill();
815  filesystem::recursive_rmdir( newMaster.path().dirname() );
816  }
817  }
818 
819  if ( line.compare( 0, 2, "D:" ) )
820  {
821  errmsg += line + '\n';
822  // report.notify( line );
823  WAR << line << endl;
824  }
825  }
826 
827  int rpm_status = systemStatus();
828 
829  if ( rpm_status != 0 )
830  {
831  //TranslatorExplanation after semicolon is error message
832  ZYPP_THROW(RpmSubprocessException(std::string(_("RPM failed: ")) + (errmsg.empty() ? error_message: errmsg) ) );
833  }
834  else
835  {
836  report->progress( 100, root() + dbPath() ); // 100%
837  }
838 }
839 
841 namespace
842 {
847  void computeKeyRingSync( std::set<Edition> & rpmKeys_r, std::list<PublicKeyData> & zyppKeys_r )
848  {
850  // Remember latest release and where it ocurred
851  struct Key
852  {
853  Key()
854  : _inRpmKeys( nullptr )
855  , _inZyppKeys( nullptr )
856  {}
857 
858  void updateIf( const Edition & rpmKey_r )
859  {
860  std::string keyRelease( rpmKey_r.release() );
861  int comp = _release.compare( keyRelease );
862  if ( comp < 0 )
863  {
864  // update to newer release
865  _release.swap( keyRelease );
866  _inRpmKeys = &rpmKey_r;
867  _inZyppKeys = nullptr;
868  if ( !keyRelease.empty() )
869  DBG << "Old key in Z: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
870  }
871  else if ( comp == 0 )
872  {
873  // stay with this release
874  if ( ! _inRpmKeys )
875  _inRpmKeys = &rpmKey_r;
876  }
877  // else: this is an old release
878  else
879  DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl;
880  }
881 
882  void updateIf( const PublicKeyData & zyppKey_r )
883  {
884  std::string keyRelease( zyppKey_r.gpgPubkeyRelease() );
885  int comp = _release.compare( keyRelease );
886  if ( comp < 0 )
887  {
888  // update to newer release
889  _release.swap( keyRelease );
890  _inRpmKeys = nullptr;
891  _inZyppKeys = &zyppKey_r;
892  if ( !keyRelease.empty() )
893  DBG << "Old key in R: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
894  }
895  else if ( comp == 0 )
896  {
897  // stay with this release
898  if ( ! _inZyppKeys )
899  _inZyppKeys = &zyppKey_r;
900  }
901  // else: this is an old release
902  else
903  DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
904  }
905 
906  std::string _release;
907  const Edition * _inRpmKeys;
908  const PublicKeyData * _inZyppKeys;
909  };
911 
912  // collect keys by ID(version) and latest creation(release)
913  std::map<std::string,Key> _keymap;
914 
915  for_( it, rpmKeys_r.begin(), rpmKeys_r.end() )
916  {
917  _keymap[(*it).version()].updateIf( *it );
918  }
919 
920  for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
921  {
922  _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
923  }
924 
925  // compute missing keys
926  std::set<Edition> rpmKeys;
927  std::list<PublicKeyData> zyppKeys;
928  for_( it, _keymap.begin(), _keymap.end() )
929  {
930  DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
931  << ( (*it).second._inRpmKeys ? "R" : "_" )
932  << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
933  if ( ! (*it).second._inRpmKeys )
934  {
935  zyppKeys.push_back( *(*it).second._inZyppKeys );
936  }
937  if ( ! (*it).second._inZyppKeys )
938  {
939  rpmKeys.insert( *(*it).second._inRpmKeys );
940  }
941  }
942  rpmKeys_r.swap( rpmKeys );
943  zyppKeys_r.swap( zyppKeys );
944  }
945 } // namespace
947 
949 {
950  MIL << "Going to sync trusted keys..." << endl;
951  std::set<Edition> rpmKeys( pubkeyEditions() );
952  std::list<PublicKeyData> zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() );
953 
954  if ( ! ( mode_r & SYNC_FROM_KEYRING ) )
955  {
956  // bsc#1064380: We relief PK from removing excess keys in the zypp keyring
957  // when re-acquiring the zyppp lock. For now we remove all excess keys.
958  // TODO: Once we can safely assume that all PK versions are updated we
959  // can think about re-importing newer key versions found in the zypp keyring and
960  // removing only excess ones (but case is not very likely). Unfixed PK versions
961  // however will remove the newer version found in the zypp keyring and by doing
962  // this, the key here will be removed via callback as well (keys are deleted
963  // via gpg id, regardless of the edition).
964  MIL << "Removing excess keys in zypp trusted keyring" << std::endl;
965  // Temporarily disconnect to prevent the attempt to pass back the delete request.
967  bool dirty = false;
968  for ( const PublicKeyData & keyData : zyppKeys )
969  {
970  if ( ! rpmKeys.count( keyData.gpgPubkeyEdition() ) )
971  {
972  DBG << "Excess key in Z to delete: gpg-pubkey-" << keyData.gpgPubkeyEdition() << endl;
973  getZYpp()->keyRing()->deleteKey( keyData.id(), /*trusted*/true );
974  if ( !dirty ) dirty = true;
975  }
976  }
977  if ( dirty )
978  zyppKeys = getZYpp()->keyRing()->trustedPublicKeyData();
979  }
980 
981  computeKeyRingSync( rpmKeys, zyppKeys );
982  MIL << (mode_r & SYNC_TO_KEYRING ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl;
983  MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl;
984 
986  if ( (mode_r & SYNC_TO_KEYRING) && ! rpmKeys.empty() )
987  {
988  // export to zypp keyring
989  MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
990  // Temporarily disconnect to prevent the attempt to re-import the exported keys.
992  librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
993 
994  TmpFile tmpfile( getZYpp()->tmpPath() );
995  {
996  std::ofstream tmpos( tmpfile.path().c_str() );
997  for_( it, rpmKeys.begin(), rpmKeys.end() )
998  {
999  // we export the rpm key into a file
1000  RpmHeader::constPtr result;
1001  getData( "gpg-pubkey", *it, result );
1002  tmpos << result->tag_description() << endl;
1003  }
1004  }
1005  try
1006  {
1007  getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
1008  }
1009  catch (Exception &e)
1010  {
1011  ERR << "Could not import keys into in zypp keyring" << endl;
1012  }
1013  }
1014 
1016  if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
1017  {
1018  // import from zypp keyring
1019  MIL << "Importing zypp trusted keyring" << std::endl;
1020  for_( it, zyppKeys.begin(), zyppKeys.end() )
1021  {
1022  try
1023  {
1024  importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
1025  }
1026  catch ( const RpmException & exp )
1027  {
1028  ZYPP_CAUGHT( exp );
1029  }
1030  }
1031  }
1032  MIL << "Trusted keys synced." << endl;
1033 }
1034 
1037 
1040 
1042 //
1043 //
1044 // METHOD NAME : RpmDb::importPubkey
1045 // METHOD TYPE : PMError
1046 //
1047 void RpmDb::importPubkey( const PublicKey & pubkey_r )
1048 {
1050 
1051  // bnc#828672: On the fly key import in READONLY
1053  {
1054  WAR << "Key " << pubkey_r << " can not be imported. (READONLY MODE)" << endl;
1055  return;
1056  }
1057 
1058  // check if the key is already in the rpm database
1059  Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
1060  std::set<Edition> rpmKeys = pubkeyEditions();
1061  bool hasOldkeys = false;
1062 
1063  for_( it, rpmKeys.begin(), rpmKeys.end() )
1064  {
1065  // bsc#1008325: Keys using subkeys for signing don't get a higher release
1066  // if new subkeys are added, because the primary key remains unchanged.
1067  // For now always re-import keys with subkeys. Here we don't want to export the
1068  // keys in the rpm database to check whether the subkeys are the same. The calling
1069  // code should take care, we don't re-import the same kesy over and over again.
1070  if ( keyEd == *it && !pubkey_r.hasSubkeys() ) // quick test (Edition is IdStringType!)
1071  {
1072  MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
1073  return;
1074  }
1075 
1076  if ( keyEd.version() != (*it).version() )
1077  continue; // different key ID (version)
1078 
1079  if ( keyEd.release() < (*it).release() )
1080  {
1081  MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl;
1082  return;
1083  }
1084  else
1085  {
1086  hasOldkeys = true;
1087  }
1088  }
1089  MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl;
1090 
1091  if ( hasOldkeys )
1092  {
1093  // We must explicitly delete old key IDs first (all releases,
1094  // that's why we don't call removePubkey here).
1095  std::string keyName( "gpg-pubkey-" + keyEd.version() );
1096  RpmArgVec opts;
1097  opts.push_back ( "-e" );
1098  opts.push_back ( "--allmatches" );
1099  opts.push_back ( "--" );
1100  opts.push_back ( keyName.c_str() );
1101  // don't call modifyDatabase because it would remove the old
1102  // rpm3 database, if the current database is a temporary one.
1104 
1105  std::string line;
1106  while ( systemReadLine( line ) )
1107  {
1108  ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
1109  }
1110 
1111  if ( systemStatus() != 0 )
1112  {
1113  ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl;
1114  }
1115  else
1116  {
1117  MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1118  }
1119  }
1120 
1121  // import the new key
1122  RpmArgVec opts;
1123  opts.push_back ( "--import" );
1124  opts.push_back ( "--" );
1125  std::string pubkeypath( pubkey_r.path().asString() );
1126  opts.push_back ( pubkeypath.c_str() );
1127 
1128  // don't call modifyDatabase because it would remove the old
1129  // rpm3 database, if the current database is a temporary one.
1131 
1132  std::string line;
1133  std::vector<std::string> excplines;
1134  while ( systemReadLine( line ) )
1135  {
1136  if ( str::startsWith( line, "error:" ) )
1137  {
1138  WAR << line << endl;
1139  excplines.push_back( std::move(line) );
1140  }
1141  else
1142  DBG << line << endl;
1143  }
1144 
1145  if ( systemStatus() != 0 )
1146  {
1147  // Translator: %1% is a gpg public key
1148  RpmSubprocessException excp( str::Format(_("Failed to import public key %1%") ) % pubkey_r.asString() );
1149  excp.moveToHistory( excplines );
1150  excp.addHistory( std::move(error_message) );
1151  ZYPP_THROW( std::move(excp) );
1152  }
1153  else
1154  {
1155  MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
1156  }
1157 }
1158 
1160 //
1161 //
1162 // METHOD NAME : RpmDb::removePubkey
1163 // METHOD TYPE : PMError
1164 //
1165 void RpmDb::removePubkey( const PublicKey & pubkey_r )
1166 {
1168 
1169  // check if the key is in the rpm database and just
1170  // return if it does not.
1171  std::set<Edition> rpm_keys = pubkeyEditions();
1172  std::set<Edition>::const_iterator found_edition = rpm_keys.end();
1173  std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
1174 
1175  for_( it, rpm_keys.begin(), rpm_keys.end() )
1176  {
1177  if ( (*it).version() == pubkeyVersion )
1178  {
1179  found_edition = it;
1180  break;
1181  }
1182  }
1183 
1184  // the key does not exist, cannot be removed
1185  if (found_edition == rpm_keys.end())
1186  {
1187  WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
1188  return;
1189  }
1190 
1191  std::string rpm_name("gpg-pubkey-" + found_edition->asString());
1192 
1193  RpmArgVec opts;
1194  opts.push_back ( "-e" );
1195  opts.push_back ( "--" );
1196  opts.push_back ( rpm_name.c_str() );
1197 
1198  // don't call modifyDatabase because it would remove the old
1199  // rpm3 database, if the current database is a temporary one.
1201 
1202  std::string line;
1203  std::vector<std::string> excplines;
1204  while ( systemReadLine( line ) )
1205  {
1206  if ( str::startsWith( line, "error:" ) )
1207  {
1208  WAR << line << endl;
1209  excplines.push_back( std::move(line) );
1210  }
1211  else
1212  DBG << line << endl;
1213  }
1214 
1215  if ( systemStatus() != 0 )
1216  {
1217  // Translator: %1% is a gpg public key
1218  RpmSubprocessException excp( str::Format(_("Failed to remove public key %1%") ) % pubkey_r.asString() );
1219  excp.moveToHistory( excplines );
1220  excp.addHistory( std::move(error_message) );
1221  ZYPP_THROW( std::move(excp) );
1222  }
1223  else
1224  {
1225  MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
1226  }
1227 }
1228 
1230 //
1231 //
1232 // METHOD NAME : RpmDb::pubkeys
1233 // METHOD TYPE : std::set<Edition>
1234 //
1235 std::list<PublicKey> RpmDb::pubkeys() const
1236 {
1237  std::list<PublicKey> ret;
1238 
1240  for ( it.findByName( "gpg-pubkey" ); *it; ++it )
1241  {
1242  Edition edition = it->tag_edition();
1243  if (edition != Edition::noedition)
1244  {
1245  // we export the rpm key into a file
1246  RpmHeader::constPtr result;
1247  getData( "gpg-pubkey", edition, result );
1248  TmpFile file(getZYpp()->tmpPath());
1249  std::ofstream os;
1250  try
1251  {
1252  os.open(file.path().asString().c_str());
1253  // dump rpm key into the tmp file
1254  os << result->tag_description();
1255  //MIL << "-----------------------------------------------" << endl;
1256  //MIL << result->tag_description() <<endl;
1257  //MIL << "-----------------------------------------------" << endl;
1258  os.close();
1259  // read the public key from the dumped file
1260  PublicKey key(file);
1261  ret.push_back(key);
1262  }
1263  catch ( std::exception & e )
1264  {
1265  ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
1266  // just ignore the key
1267  }
1268  }
1269  }
1270  return ret;
1271 }
1272 
1273 std::set<Edition> RpmDb::pubkeyEditions() const
1274  {
1275  std::set<Edition> ret;
1276 
1278  for ( it.findByName( "gpg-pubkey" ); *it; ++it )
1279  {
1280  Edition edition = it->tag_edition();
1281  if (edition != Edition::noedition)
1282  ret.insert( edition );
1283  }
1284  return ret;
1285  }
1286 
1287 
1289 //
1290 //
1291 // METHOD NAME : RpmDb::fileList
1292 // METHOD TYPE : bool
1293 //
1294 // DESCRIPTION :
1295 //
1296 std::list<FileInfo>
1297 RpmDb::fileList( const std::string & name_r, const Edition & edition_r ) const
1298 {
1299  std::list<FileInfo> result;
1300 
1302  bool found;
1303  if (edition_r == Edition::noedition)
1304  {
1305  found = it.findPackage( name_r );
1306  }
1307  else
1308  {
1309  found = it.findPackage( name_r, edition_r );
1310  }
1311  if (!found)
1312  return result;
1313 
1314  return result;
1315 }
1316 
1317 
1319 //
1320 //
1321 // METHOD NAME : RpmDb::hasFile
1322 // METHOD TYPE : bool
1323 //
1324 // DESCRIPTION :
1325 //
1326 bool RpmDb::hasFile( const std::string & file_r, const std::string & name_r ) const
1327 {
1329  bool res;
1330  do
1331  {
1332  res = it.findByFile( file_r );
1333  if (!res) break;
1334  if (!name_r.empty())
1335  {
1336  res = (it->tag_name() == name_r);
1337  }
1338  ++it;
1339  }
1340  while (res && *it);
1341  return res;
1342 }
1343 
1345 //
1346 //
1347 // METHOD NAME : RpmDb::whoOwnsFile
1348 // METHOD TYPE : std::string
1349 //
1350 // DESCRIPTION :
1351 //
1352 std::string RpmDb::whoOwnsFile( const std::string & file_r) const
1353 {
1355  if (it.findByFile( file_r ))
1356  {
1357  return it->tag_name();
1358  }
1359  return "";
1360 }
1361 
1363 //
1364 //
1365 // METHOD NAME : RpmDb::hasProvides
1366 // METHOD TYPE : bool
1367 //
1368 // DESCRIPTION :
1369 //
1370 bool RpmDb::hasProvides( const std::string & tag_r ) const
1371 {
1373  return it.findByProvides( tag_r );
1374 }
1375 
1377 //
1378 //
1379 // METHOD NAME : RpmDb::hasRequiredBy
1380 // METHOD TYPE : bool
1381 //
1382 // DESCRIPTION :
1383 //
1384 bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
1385 {
1387  return it.findByRequiredBy( tag_r );
1388 }
1389 
1391 //
1392 //
1393 // METHOD NAME : RpmDb::hasConflicts
1394 // METHOD TYPE : bool
1395 //
1396 // DESCRIPTION :
1397 //
1398 bool RpmDb::hasConflicts( const std::string & tag_r ) const
1399 {
1401  return it.findByConflicts( tag_r );
1402 }
1403 
1405 //
1406 //
1407 // METHOD NAME : RpmDb::hasPackage
1408 // METHOD TYPE : bool
1409 //
1410 // DESCRIPTION :
1411 //
1412 bool RpmDb::hasPackage( const std::string & name_r ) const
1413 {
1415  return it.findPackage( name_r );
1416 }
1417 
1419 //
1420 //
1421 // METHOD NAME : RpmDb::hasPackage
1422 // METHOD TYPE : bool
1423 //
1424 // DESCRIPTION :
1425 //
1426 bool RpmDb::hasPackage( const std::string & name_r, const Edition & ed_r ) const
1427 {
1429  return it.findPackage( name_r, ed_r );
1430 }
1431 
1433 //
1434 //
1435 // METHOD NAME : RpmDb::getData
1436 // METHOD TYPE : PMError
1437 //
1438 // DESCRIPTION :
1439 //
1440 void RpmDb::getData( const std::string & name_r,
1441  RpmHeader::constPtr & result_r ) const
1442 {
1444  it.findPackage( name_r );
1445  result_r = *it;
1446  if (it.dbError())
1447  ZYPP_THROW(*(it.dbError()));
1448 }
1449 
1451 //
1452 //
1453 // METHOD NAME : RpmDb::getData
1454 // METHOD TYPE : void
1455 //
1456 // DESCRIPTION :
1457 //
1458 void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
1459  RpmHeader::constPtr & result_r ) const
1460 {
1462  it.findPackage( name_r, ed_r );
1463  result_r = *it;
1464  if (it.dbError())
1465  ZYPP_THROW(*(it.dbError()));
1466 }
1467 
1469 namespace
1470 {
1471  struct RpmlogCapture : public std::string
1472  {
1473  RpmlogCapture()
1474  { rpmlog()._cap = this; }
1475 
1476  ~RpmlogCapture()
1477  { rpmlog()._cap = nullptr; }
1478 
1479  private:
1480  struct Rpmlog
1481  {
1482  Rpmlog()
1483  : _cap( nullptr )
1484  {
1485  rpmlogSetCallback( rpmLogCB, this );
1486  rpmSetVerbosity( RPMLOG_INFO );
1487  _f = ::fopen( "/dev/null","w");
1488  rpmlogSetFile( _f );
1489  }
1490 
1491  ~Rpmlog()
1492  { if ( _f ) ::fclose( _f ); }
1493 
1494  static int rpmLogCB( rpmlogRec rec_r, rpmlogCallbackData data_r )
1495  { return reinterpret_cast<Rpmlog*>(data_r)->rpmLog( rec_r ); }
1496 
1497  int rpmLog( rpmlogRec rec_r )
1498  {
1499  if ( _cap ) (*_cap) += rpmlogRecMessage( rec_r );
1500  return RPMLOG_DEFAULT;
1501  }
1502 
1503  FILE * _f;
1504  std::string * _cap;
1505  };
1506 
1507  static Rpmlog & rpmlog()
1508  { static Rpmlog _rpmlog; return _rpmlog; }
1509  };
1510 
1511  RpmDb::CheckPackageResult doCheckPackageSig( const Pathname & path_r, // rpm file to check
1512  const Pathname & root_r, // target root
1513  bool requireGPGSig_r, // whether no gpg signature is to be reported
1514  RpmDb::CheckPackageDetail & detail_r ) // detailed result
1515  {
1516  PathInfo file( path_r );
1517  if ( ! file.isFile() )
1518  {
1519  ERR << "Not a file: " << file << endl;
1520  return RpmDb::CHK_ERROR;
1521  }
1522 
1523  FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
1524  if ( fd == 0 || ::Ferror(fd) )
1525  {
1526  ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
1527  if ( fd )
1528  ::Fclose( fd );
1529  return RpmDb::CHK_ERROR;
1530  }
1531  rpmts ts = ::rpmtsCreate();
1532  ::rpmtsSetRootDir( ts, root_r.c_str() );
1533  ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
1534 
1535  rpmQVKArguments_s qva;
1536  memset( &qva, 0, sizeof(rpmQVKArguments_s) );
1537  qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
1538 
1539  RpmlogCapture vresult;
1540  LocaleGuard guard( LC_ALL, "C" ); // bsc#1076415: rpm log output is localized, but we need to parse it :(
1541  int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
1542  guard.restore();
1543 
1544  ts = rpmtsFree(ts);
1545  ::Fclose( fd );
1546 
1547  // results per line...
1548  // Header V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1549  // Header SHA1 digest: OK (a60386347863affefef484ff1f26c889373eb094)
1550  // V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
1551  // MD5 digest: OK (fd5259fe677a406951dcb2e9d08c4dcc)
1552  //
1553  // TODO: try to get SIG info from the header rather than parsing the output
1554  std::vector<std::string> lines;
1555  str::split( vresult, std::back_inserter(lines), "\n" );
1556  unsigned count[7] = { 0, 0, 0, 0, 0, 0, 0 };
1557 
1558  for ( unsigned i = 1; i < lines.size(); ++i )
1559  {
1560  std::string & line( lines[i] );
1562  if ( line.find( ": OK" ) != std::string::npos )
1563  {
1564  lineres = RpmDb::CHK_OK;
1565  if ( line.find( "Signature, key ID" ) == std::string::npos )
1566  ++count[RpmDb::CHK_NOSIG]; // Valid but no gpg signature -> CHK_NOSIG
1567  }
1568  else if ( line.find( ": NOKEY" ) != std::string::npos )
1569  { lineres = RpmDb::CHK_NOKEY; }
1570  else if ( line.find( ": BAD" ) != std::string::npos )
1571  { lineres = RpmDb::CHK_FAIL; }
1572  else if ( line.find( ": UNKNOWN" ) != std::string::npos )
1573  { lineres = RpmDb::CHK_NOTFOUND; }
1574  else if ( line.find( ": NOTRUSTED" ) != std::string::npos )
1575  { lineres = RpmDb::CHK_NOTTRUSTED; }
1576 
1577  ++count[lineres];
1578  detail_r.push_back( RpmDb::CheckPackageDetail::value_type( lineres, std::move(line) ) );
1579  }
1580 
1582 
1583  if ( count[RpmDb::CHK_FAIL] )
1584  ret = RpmDb::CHK_FAIL;
1585 
1586  else if ( count[RpmDb::CHK_NOTFOUND] )
1587  ret = RpmDb::CHK_NOTFOUND;
1588 
1589  else if ( count[RpmDb::CHK_NOKEY] )
1590  ret = RpmDb::CHK_NOKEY;
1591 
1592  else if ( count[RpmDb::CHK_NOTTRUSTED] )
1593  ret = RpmDb::CHK_NOTTRUSTED;
1594 
1595  else if ( ret == RpmDb::CHK_OK )
1596  {
1597  if ( count[RpmDb::CHK_OK] == count[RpmDb::CHK_NOSIG] )
1598  {
1599  detail_r.push_back( RpmDb::CheckPackageDetail::value_type( RpmDb::CHK_NOSIG, std::string(" ")+_("Package is not signed!") ) );
1600  if ( requireGPGSig_r )
1601  ret = RpmDb::CHK_NOSIG;
1602  }
1603  }
1604 
1605  if ( ret != RpmDb::CHK_OK )
1606  {
1607  WAR << path_r << " (" << requireGPGSig_r << " -> " << ret << ")" << endl;
1608  WAR << vresult;
1609  }
1610  return ret;
1611  }
1612 
1613 } // namespace
1615 //
1616 // METHOD NAME : RpmDb::checkPackage
1617 // METHOD TYPE : RpmDb::CheckPackageResult
1618 //
1620 { return doCheckPackageSig( path_r, root(), false/*requireGPGSig_r*/, detail_r ); }
1621 
1623 { CheckPackageDetail dummy; return checkPackage( path_r, dummy ); }
1624 
1626 { return doCheckPackageSig( path_r, root(), true/*requireGPGSig_r*/, detail_r ); }
1627 
1628 
1629 // determine changed files of installed package
1630 bool
1631 RpmDb::queryChangedFiles(FileList & fileList, const std::string& packageName)
1632 {
1633  bool ok = true;
1634 
1635  fileList.clear();
1636 
1637  if ( ! initialized() ) return false;
1638 
1639  RpmArgVec opts;
1640 
1641  opts.push_back ("-V");
1642  opts.push_back ("--nodeps");
1643  opts.push_back ("--noscripts");
1644  opts.push_back ("--nomd5");
1645  opts.push_back ("--");
1646  opts.push_back (packageName.c_str());
1647 
1649 
1650  if ( process == NULL )
1651  return false;
1652 
1653  /* from rpm manpage
1654  5 MD5 sum
1655  S File size
1656  L Symlink
1657  T Mtime
1658  D Device
1659  U User
1660  G Group
1661  M Mode (includes permissions and file type)
1662  */
1663 
1664  std::string line;
1665  while (systemReadLine(line))
1666  {
1667  if (line.length() > 12 &&
1668  (line[0] == 'S' || line[0] == 's' ||
1669  (line[0] == '.' && line[7] == 'T')))
1670  {
1671  // file has been changed
1672  std::string filename;
1673 
1674  filename.assign(line, 11, line.length() - 11);
1675  fileList.insert(filename);
1676  }
1677  }
1678 
1679  systemStatus();
1680  // exit code ignored, rpm returns 1 no matter if package is installed or
1681  // not
1682 
1683  return ok;
1684 }
1685 
1686 
1687 
1688 /****************************************************************/
1689 /* private member-functions */
1690 /****************************************************************/
1691 
1692 /*--------------------------------------------------------------*/
1693 /* Run rpm with the specified arguments, handling stderr */
1694 /* as specified by disp */
1695 /*--------------------------------------------------------------*/
1696 void
1699 {
1700  if ( process )
1701  {
1702  delete process;
1703  process = NULL;
1704  }
1705  exit_code = -1;
1706 
1707  if ( ! initialized() )
1708  {
1710  }
1711 
1712  RpmArgVec args;
1713 
1714  // always set root and dbpath
1715 #if defined(WORKAROUNDRPMPWDBUG)
1716  args.push_back("#/"); // chdir to / to workaround bnc#819354
1717 #endif
1718  args.push_back("rpm");
1719  args.push_back("--root");
1720  args.push_back(_root.asString().c_str());
1721  args.push_back("--dbpath");
1722  args.push_back(_dbPath.asString().c_str());
1723 
1724  const char* argv[args.size() + opts.size() + 1];
1725 
1726  const char** p = argv;
1727  p = copy (args.begin (), args.end (), p);
1728  p = copy (opts.begin (), opts.end (), p);
1729  *p = 0;
1730 
1731  // Invalidate all outstanding database handles in case
1732  // the database gets modified.
1733  librpmDb::dbRelease( true );
1734 
1735  // Launch the program with default locale
1736  process = new ExternalProgram(argv, disp, false, -1, true);
1737  return;
1738 }
1739 
1740 /*--------------------------------------------------------------*/
1741 /* Read a line from the rpm process */
1742 /*--------------------------------------------------------------*/
1743 bool RpmDb::systemReadLine( std::string & line )
1744 {
1745  line.erase();
1746 
1747  if ( process == NULL )
1748  return false;
1749 
1750  if ( process->inputFile() )
1751  {
1752  process->setBlocking( false );
1753  FILE * inputfile = process->inputFile();
1754  int inputfileFd = ::fileno( inputfile );
1755  do
1756  {
1757  /* Watch inputFile to see when it has input. */
1758  fd_set rfds;
1759  FD_ZERO( &rfds );
1760  FD_SET( inputfileFd, &rfds );
1761 
1762  /* Wait up to 5 seconds. */
1763  struct timeval tv;
1764  tv.tv_sec = 5;
1765  tv.tv_usec = 0;
1766 
1767  int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
1768 
1769  if ( retval == -1 )
1770  {
1771  ERR << "select error: " << strerror(errno) << endl;
1772  if ( errno != EINTR )
1773  return false;
1774  }
1775  else if ( retval )
1776  {
1777  // Data is available now.
1778  static size_t linebuffer_size = 0; // static because getline allocs
1779  static char * linebuffer = 0; // and reallocs if buffer is too small
1780  ssize_t nread = getline( &linebuffer, &linebuffer_size, inputfile );
1781  if ( nread == -1 )
1782  {
1783  if ( ::feof( inputfile ) )
1784  return line.size(); // in case of pending output
1785  }
1786  else
1787  {
1788  if ( nread > 0 )
1789  {
1790  if ( linebuffer[nread-1] == '\n' )
1791  --nread;
1792  line += std::string( linebuffer, nread );
1793  }
1794 
1795  if ( ! ::ferror( inputfile ) || ::feof( inputfile ) )
1796  return true; // complete line
1797  }
1798  clearerr( inputfile );
1799  }
1800  else
1801  {
1802  // No data within time.
1803  if ( ! process->running() )
1804  return false;
1805  }
1806  } while ( true );
1807  }
1808 
1809  return false;
1810 }
1811 
1812 /*--------------------------------------------------------------*/
1813 /* Return the exit status of the rpm process, closing the */
1814 /* connection if not already done */
1815 /*--------------------------------------------------------------*/
1816 int
1818 {
1819  if ( process == NULL )
1820  return -1;
1821 
1822  exit_code = process->close();
1823  if (exit_code == 0)
1824  error_message = "";
1825  else
1827  process->kill();
1828  delete process;
1829  process = 0;
1830 
1831  // DBG << "exit code " << exit_code << endl;
1832 
1833  return exit_code;
1834 }
1835 
1836 /*--------------------------------------------------------------*/
1837 /* Forcably kill the rpm process */
1838 /*--------------------------------------------------------------*/
1839 void
1841 {
1842  if (process) process->kill();
1843 }
1844 
1845 
1846 // generate diff mails for config files
1847 void RpmDb::processConfigFiles(const std::string& line, const std::string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1848 {
1849  std::string msg = line.substr(9);
1850  std::string::size_type pos1 = std::string::npos;
1851  std::string::size_type pos2 = std::string::npos;
1852  std::string file1s, file2s;
1853  Pathname file1;
1854  Pathname file2;
1855 
1856  pos1 = msg.find (typemsg);
1857  for (;;)
1858  {
1859  if ( pos1 == std::string::npos )
1860  break;
1861 
1862  pos2 = pos1 + strlen (typemsg);
1863 
1864  if (pos2 >= msg.length() )
1865  break;
1866 
1867  file1 = msg.substr (0, pos1);
1868  file2 = msg.substr (pos2);
1869 
1870  file1s = file1.asString();
1871  file2s = file2.asString();
1872 
1873  if (!_root.empty() && _root != "/")
1874  {
1875  file1 = _root + file1;
1876  file2 = _root + file2;
1877  }
1878 
1879  std::string out;
1880  int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1881  if (ret)
1882  {
1883  Pathname file = _root + WARNINGMAILPATH;
1884  if (filesystem::assert_dir(file) != 0)
1885  {
1886  ERR << "Could not create " << file.asString() << endl;
1887  break;
1888  }
1889  file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1890  std::ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
1891  if (!notify)
1892  {
1893  ERR << "Could not open " << file << endl;
1894  break;
1895  }
1896 
1897  // Translator: %s = name of an rpm package. A list of diffs follows
1898  // this message.
1899  notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1900  if (ret>1)
1901  {
1902  ERR << "diff failed" << endl;
1903  notify << str::form(difffailmsg,
1904  file1s.c_str(), file2s.c_str()) << endl;
1905  }
1906  else
1907  {
1908  notify << str::form(diffgenmsg,
1909  file1s.c_str(), file2s.c_str()) << endl;
1910 
1911  // remove root for the viewer's pleasure (#38240)
1912  if (!_root.empty() && _root != "/")
1913  {
1914  if (out.substr(0,4) == "--- ")
1915  {
1916  out.replace(4, file1.asString().length(), file1s);
1917  }
1918  std::string::size_type pos = out.find("\n+++ ");
1919  if (pos != std::string::npos)
1920  {
1921  out.replace(pos+5, file2.asString().length(), file2s);
1922  }
1923  }
1924  notify << out << endl;
1925  }
1926  notify.close();
1927  notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1928  notify.close();
1929  }
1930  else
1931  {
1932  WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1933  }
1934  break;
1935  }
1936 }
1937 
1939 //
1940 //
1941 // METHOD NAME : RpmDb::installPackage
1942 // METHOD TYPE : PMError
1943 //
1944 void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
1945 {
1947 
1948  report->start(filename);
1949 
1950  do
1951  try
1952  {
1953  doInstallPackage(filename, flags, report);
1954  report->finish();
1955  break;
1956  }
1957  catch (RpmException & excpt_r)
1958  {
1959  RpmInstallReport::Action user = report->problem( excpt_r );
1960 
1961  if ( user == RpmInstallReport::ABORT )
1962  {
1963  report->finish( excpt_r );
1964  ZYPP_RETHROW(excpt_r);
1965  }
1966  else if ( user == RpmInstallReport::IGNORE )
1967  {
1968  break;
1969  }
1970  }
1971  while (true);
1972 }
1973 
1975 {
1977  HistoryLog historylog;
1978 
1979  MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1980 
1981 
1982  // backup
1983  if ( _packagebackups )
1984  {
1985  // FIXME report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1986  if ( ! backupPackage( filename ) )
1987  {
1988  ERR << "backup of " << filename.asString() << " failed" << endl;
1989  }
1990  // FIXME status handling
1991  report->progress( 0 ); // allow 1% for backup creation.
1992  }
1993 
1994  // run rpm
1995  RpmArgVec opts;
1996  if (flags & RPMINST_NOUPGRADE)
1997  opts.push_back("-i");
1998  else
1999  opts.push_back("-U");
2000 
2001  opts.push_back("--percent");
2002  opts.push_back("--noglob");
2003 
2004  // ZConfig defines cross-arch installation
2005  if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
2006  opts.push_back("--ignorearch");
2007 
2008  if (flags & RPMINST_NODIGEST)
2009  opts.push_back("--nodigest");
2010  if (flags & RPMINST_NOSIGNATURE)
2011  opts.push_back("--nosignature");
2012  if (flags & RPMINST_EXCLUDEDOCS)
2013  opts.push_back ("--excludedocs");
2014  if (flags & RPMINST_NOSCRIPTS)
2015  opts.push_back ("--noscripts");
2016  if (flags & RPMINST_FORCE)
2017  opts.push_back ("--force");
2018  if (flags & RPMINST_NODEPS)
2019  opts.push_back ("--nodeps");
2020  if (flags & RPMINST_IGNORESIZE)
2021  opts.push_back ("--ignoresize");
2022  if (flags & RPMINST_JUSTDB)
2023  opts.push_back ("--justdb");
2024  if (flags & RPMINST_TEST)
2025  opts.push_back ("--test");
2026  if (flags & RPMINST_NOPOSTTRANS)
2027  opts.push_back ("--noposttrans");
2028 
2029  opts.push_back("--");
2030 
2031  // rpm requires additional quoting of special chars:
2032  std::string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
2033  opts.push_back ( quotedFilename.c_str() );
2034 
2035  modifyDatabase(); // BEFORE run_rpm
2037 
2038  std::string line;
2039  std::string rpmmsg; // TODO: immediately forward lines via Callback::report rather than collecting
2040  std::vector<std::string> configwarnings; // TODO: immediately process lines rather than collecting
2041 
2042  unsigned linecnt = 0;
2043  while ( systemReadLine( line ) )
2044  {
2045  if ( str::startsWith( line, "%%" ) )
2046  {
2047  int percent;
2048  sscanf( line.c_str() + 2, "%d", &percent );
2049  report->progress( percent );
2050  continue;
2051  }
2052 
2053  if ( linecnt < MAXRPMMESSAGELINES )
2054  ++linecnt;
2055  else if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
2056  continue;
2057 
2058  rpmmsg += line+'\n';
2059 
2060  if ( str::startsWith( line, "warning:" ) )
2061  configwarnings.push_back(line);
2062  }
2063  if ( linecnt >= MAXRPMMESSAGELINES )
2064  rpmmsg += "[truncated]\n";
2065 
2066  int rpm_status = systemStatus();
2067 
2068  // evaluate result
2069  for (std::vector<std::string>::iterator it = configwarnings.begin();
2070  it != configwarnings.end(); ++it)
2071  {
2072  processConfigFiles(*it, Pathname::basename(filename), " saved as ",
2073  // %s = filenames
2074  _("rpm saved %s as %s, but it was impossible to determine the difference"),
2075  // %s = filenames
2076  _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
2077  processConfigFiles(*it, Pathname::basename(filename), " created as ",
2078  // %s = filenames
2079  _("rpm created %s as %s, but it was impossible to determine the difference"),
2080  // %s = filenames
2081  _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
2082  }
2083 
2084  if ( rpm_status != 0 )
2085  {
2086  historylog.comment(
2087  str::form("%s install failed", Pathname::basename(filename).c_str()),
2088  true /*timestamp*/);
2089  std::ostringstream sstr;
2090  sstr << "rpm output:" << endl << rpmmsg << endl;
2091  historylog.comment(sstr.str());
2092  // TranslatorExplanation the colon is followed by an error message
2093  ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message : rpmmsg) ));
2094  }
2095  else if ( ! rpmmsg.empty() )
2096  {
2097  historylog.comment(
2098  str::form("%s installed ok", Pathname::basename(filename).c_str()),
2099  true /*timestamp*/);
2100  std::ostringstream sstr;
2101  sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2102  historylog.comment(sstr.str());
2103 
2104  // report additional rpm output in finish
2105  // TranslatorExplanation Text is followed by a ':' and the actual output.
2106  report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2107  }
2108 }
2109 
2111 //
2112 //
2113 // METHOD NAME : RpmDb::removePackage
2114 // METHOD TYPE : PMError
2115 //
2116 void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
2117 {
2118  // 'rpm -e' does not like epochs
2119  return removePackage( package->name()
2120  + "-" + package->edition().version()
2121  + "-" + package->edition().release()
2122  + "." + package->arch().asString(), flags );
2123 }
2124 
2126 //
2127 //
2128 // METHOD NAME : RpmDb::removePackage
2129 // METHOD TYPE : PMError
2130 //
2131 void RpmDb::removePackage( const std::string & name_r, RpmInstFlags flags )
2132 {
2134 
2135  report->start( name_r );
2136 
2137  do
2138  try
2139  {
2140  doRemovePackage(name_r, flags, report);
2141  report->finish();
2142  break;
2143  }
2144  catch (RpmException & excpt_r)
2145  {
2146  RpmRemoveReport::Action user = report->problem( excpt_r );
2147 
2148  if ( user == RpmRemoveReport::ABORT )
2149  {
2150  report->finish( excpt_r );
2151  ZYPP_RETHROW(excpt_r);
2152  }
2153  else if ( user == RpmRemoveReport::IGNORE )
2154  {
2155  break;
2156  }
2157  }
2158  while (true);
2159 }
2160 
2161 
2162 void RpmDb::doRemovePackage( const std::string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
2163 {
2165  HistoryLog historylog;
2166 
2167  MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
2168 
2169  // backup
2170  if ( _packagebackups )
2171  {
2172  // FIXME solve this status report somehow
2173  // report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
2174  if ( ! backupPackage( name_r ) )
2175  {
2176  ERR << "backup of " << name_r << " failed" << endl;
2177  }
2178  report->progress( 0 );
2179  }
2180  else
2181  {
2182  report->progress( 100 );
2183  }
2184 
2185  // run rpm
2186  RpmArgVec opts;
2187  opts.push_back("-e");
2188  opts.push_back("--allmatches");
2189 
2190  if (flags & RPMINST_NOSCRIPTS)
2191  opts.push_back("--noscripts");
2192  if (flags & RPMINST_NODEPS)
2193  opts.push_back("--nodeps");
2194  if (flags & RPMINST_JUSTDB)
2195  opts.push_back("--justdb");
2196  if (flags & RPMINST_TEST)
2197  opts.push_back ("--test");
2198  if (flags & RPMINST_FORCE)
2199  {
2200  WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
2201  }
2202 
2203  opts.push_back("--");
2204  opts.push_back(name_r.c_str());
2205 
2206  modifyDatabase(); // BEFORE run_rpm
2208 
2209  std::string line;
2210  std::string rpmmsg; // TODO: immediately forward lines via Callback::report rather than collecting
2211 
2212  // got no progress from command, so we fake it:
2213  // 5 - command started
2214  // 50 - command completed
2215  // 100 if no error
2216  report->progress( 5 );
2217  unsigned linecnt = 0;
2218  while (systemReadLine(line))
2219  {
2220  if ( linecnt < MAXRPMMESSAGELINES )
2221  ++linecnt;
2222  else if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
2223  continue;
2224  rpmmsg += line+'\n';
2225  }
2226  if ( linecnt >= MAXRPMMESSAGELINES )
2227  rpmmsg += "[truncated]\n";
2228  report->progress( 50 );
2229  int rpm_status = systemStatus();
2230 
2231  if ( rpm_status != 0 )
2232  {
2233  historylog.comment(
2234  str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
2235  std::ostringstream sstr;
2236  sstr << "rpm output:" << endl << rpmmsg << endl;
2237  historylog.comment(sstr.str());
2238  // TranslatorExplanation the colon is followed by an error message
2239  ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message: rpmmsg) ));
2240  }
2241  else if ( ! rpmmsg.empty() )
2242  {
2243  historylog.comment(
2244  str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
2245 
2246  std::ostringstream sstr;
2247  sstr << "Additional rpm output:" << endl << rpmmsg << endl;
2248  historylog.comment(sstr.str());
2249 
2250  // report additional rpm output in finish
2251  // TranslatorExplanation Text is followed by a ':' and the actual output.
2252  report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"), rpmmsg.c_str() ));
2253  }
2254 }
2255 
2257 //
2258 //
2259 // METHOD NAME : RpmDb::backupPackage
2260 // METHOD TYPE : bool
2261 //
2262 bool RpmDb::backupPackage( const Pathname & filename )
2263 {
2265  if ( ! h )
2266  return false;
2267 
2268  return backupPackage( h->tag_name() );
2269 }
2270 
2272 //
2273 //
2274 // METHOD NAME : RpmDb::backupPackage
2275 // METHOD TYPE : bool
2276 //
2277 bool RpmDb::backupPackage(const std::string& packageName)
2278 {
2279  HistoryLog progresslog;
2280  bool ret = true;
2281  Pathname backupFilename;
2282  Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
2283 
2284  if (_backuppath.empty())
2285  {
2286  INT << "_backuppath empty" << endl;
2287  return false;
2288  }
2289 
2291 
2292  if (!queryChangedFiles(fileList, packageName))
2293  {
2294  ERR << "Error while getting changed files for package " <<
2295  packageName << endl;
2296  return false;
2297  }
2298 
2299  if (fileList.size() <= 0)
2300  {
2301  DBG << "package " << packageName << " not changed -> no backup" << endl;
2302  return true;
2303  }
2304 
2306  {
2307  return false;
2308  }
2309 
2310  {
2311  // build up archive name
2312  time_t currentTime = time(0);
2313  struct tm *currentLocalTime = localtime(&currentTime);
2314 
2315  int date = (currentLocalTime->tm_year + 1900) * 10000
2316  + (currentLocalTime->tm_mon + 1) * 100
2317  + currentLocalTime->tm_mday;
2318 
2319  int num = 0;
2320  do
2321  {
2322  backupFilename = _root + _backuppath
2323  + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
2324 
2325  }
2326  while ( PathInfo(backupFilename).isExist() && num++ < 1000);
2327 
2328  PathInfo pi(filestobackupfile);
2329  if (pi.isExist() && !pi.isFile())
2330  {
2331  ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
2332  return false;
2333  }
2334 
2335  std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
2336 
2337  if (!fp)
2338  {
2339  ERR << "could not open " << filestobackupfile.asString() << endl;
2340  return false;
2341  }
2342 
2343  for (FileList::const_iterator cit = fileList.begin();
2344  cit != fileList.end(); ++cit)
2345  {
2346  std::string name = *cit;
2347  if ( name[0] == '/' )
2348  {
2349  // remove slash, file must be relative to -C parameter of tar
2350  name = name.substr( 1 );
2351  }
2352  DBG << "saving file "<< name << endl;
2353  fp << name << endl;
2354  }
2355  fp.close();
2356 
2357  const char* const argv[] =
2358  {
2359  "tar",
2360  "-czhP",
2361  "-C",
2362  _root.asString().c_str(),
2363  "--ignore-failed-read",
2364  "-f",
2365  backupFilename.asString().c_str(),
2366  "-T",
2367  filestobackupfile.asString().c_str(),
2368  NULL
2369  };
2370 
2371  // execute tar in inst-sys (we dont know if there is a tar below _root !)
2372  ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2373 
2374  std::string tarmsg;
2375 
2376  // TODO: its probably possible to start tar with -v and watch it adding
2377  // files to report progress
2378  for (std::string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2379  {
2380  tarmsg+=output;
2381  }
2382 
2383  int ret = tar.close();
2384 
2385  if ( ret != 0)
2386  {
2387  ERR << "tar failed: " << tarmsg << endl;
2388  ret = false;
2389  }
2390  else
2391  {
2392  MIL << "tar backup ok" << endl;
2393  progresslog.comment(
2394  str::form(_("created backup %s"), backupFilename.asString().c_str())
2395  , /*timestamp*/true);
2396  }
2397 
2398  filesystem::unlink(filestobackupfile);
2399  }
2400 
2401  return ret;
2402 }
2403 
2405 {
2406  _backuppath = path;
2407 }
2408 
2409 std::ostream & operator<<( std::ostream & str, RpmDb::CheckPackageResult obj )
2410 {
2411  switch ( obj )
2412  {
2413 #define OUTS(E,S) case RpmDb::E: return str << "["<< (unsigned)obj << "-"<< S << "]"; break
2414  // translators: possible rpm package signature check result [brief]
2415  OUTS( CHK_OK, _("Signature is OK") );
2416  // translators: possible rpm package signature check result [brief]
2417  OUTS( CHK_NOTFOUND, _("Unknown type of signature") );
2418  // translators: possible rpm package signature check result [brief]
2419  OUTS( CHK_FAIL, _("Signature does not verify") );
2420  // translators: possible rpm package signature check result [brief]
2421  OUTS( CHK_NOTTRUSTED, _("Signature is OK, but key is not trusted") );
2422  // translators: possible rpm package signature check result [brief]
2423  OUTS( CHK_NOKEY, _("Signatures public key is not available") );
2424  // translators: possible rpm package signature check result [brief]
2425  OUTS( CHK_ERROR, _("File does not exist or signature can't be checked") );
2426  // translators: possible rpm package signature check result [brief]
2427  OUTS( CHK_NOSIG, _("File is unsigned") );
2428 #undef OUTS
2429  }
2430  return str << "UnknowSignatureCheckError("+str::numstring(obj)+")";
2431 }
2432 
2433 std::ostream & operator<<( std::ostream & str, const RpmDb::CheckPackageDetail & obj )
2434 {
2435  for ( const auto & el : obj )
2436  str << el.second << endl;
2437  return str;
2438 }
2439 
2440 } // namespace rpm
2441 } // namespace target
2442 } // namespace zypp
std::ostream & operator<<(std::ostream &str, const librpmDb::DbDirInfo &obj)
Definition: librpmDb.cc:544
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:320
Interface to gettext.
Interface to the rpm program.
Definition: RpmDb.h:47
#define MIL
Definition: Logger.h:64
unsigned diffFiles(const std::string file1, const std::string file2, std::string &out, int maxlines)
Definition: RpmDb.cc:142
CheckPackageResult checkPackageSignature(const Pathname &path_r, CheckPackageDetail &detail_r)
Check signature of rpm file on disk (strict check returning CHK_NOSIG if file is unsigned).
Definition: RpmDb.cc:1625
intrusive_ptr< const RpmHeader > constPtr
Definition: RpmHeader.h:64
bool hasRequiredBy(const std::string &tag_r) const
Return true if at least one package requires a certain tag.
Definition: RpmDb.cc:1384
static unsigned blockAccess()
Blocks further access to rpmdb.
Definition: librpmDb.cc:326
static std::ostream & dumpState(std::ostream &str)
Dump debug info.
Definition: librpmDb.cc:351
const Pathname & path() const
Return current Pathname.
Definition: PathInfo.h:246
void getData(const std::string &name_r, RpmHeader::constPtr &result_r) const
Get an installed packages data from rpmdb.
Definition: RpmDb.cc:1440
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:392
virtual void trustedKeyAdded(const PublicKey &key)
Definition: RpmDb.cc:125
bool kill()
Kill the program.
#define ENUM_OUT(B, C)
static ZConfig & instance()
Singleton ctor.
Definition: Resolver.cc:125
Pathname _root
Root directory for all operations.
Definition: RpmDb.h:96
bool findByProvides(const std::string &tag_r)
Reset to iterate all packages that provide a certain tag.
Definition: librpmDb.cc:826
const PathInfo & dbV3ToV4() const
rpmV3 database backup created on conversion to rpmV4 (_dbDir/packages.rpm3)
Definition: librpmDb.h:416
Class representing one GPG Public Keys data.
Definition: PublicKey.h:139
Collect info about what kind of rpmdb seems to be present by looking at paths and filenames...
Definition: librpmDb.h:327
std::string id() const
Definition: PublicKey.cc:511
Pathname extend(const std::string &r) const
Append string r to the last component of the path.
Definition: Pathname.h:169
void exportTrustedKeysInZyppKeyRing()
insert all rpm trusted keys into zypp trusted keyring
Definition: RpmDb.cc:1038
#define INT
Definition: Logger.h:68
static void dbAccess()
Access the database at the current default location.
Definition: librpmDb.cc:248
void rebuildDatabase()
Rebuild the rpm database (rpm –rebuilddb).
Definition: RpmDb.cc:760
void installPackage(const Pathname &filename, RpmInstFlags flags=RPMINST_NONE)
install rpm package
Definition: RpmDb.cc:1944
time_t mtime() const
Definition: PathInfo.h:376
const char * c_str() const
String representation.
Definition: Pathname.h:109
Date timestamp() const
timestamp of the rpm database (last modification)
Definition: RpmDb.cc:261
void internal_initDatabase(const Pathname &root_r, const Pathname &dbPath_r, DbStateInfoBits &info_r)
Internal helper for initDatabase.
Definition: RpmDb.cc:433
String related utilities and Regular expression matching.
bool hasDbV3() const
Whether dbV3 file exists.
Definition: librpmDb.h:466
bool findByRequiredBy(const std::string &tag_r)
Reset to iterate all packages that require a certain tag.
Definition: librpmDb.cc:837
static double currentTime()
void modifyDatabase()
Called before the database is modified by installPackage/removePackage.
Definition: RpmDb.cc:690
unsigned split(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \)
Split line_r into words.
Definition: String.h:502
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:27
Pathname path() const
Definition: TmpPath.cc:146
Edition represents [epoch:]version[-release]
Definition: Edition.h:60
bool running()
Return whether program is running.
bool usableArgs() const
Whether constructor arguments were llegal and dbDir either is a directory or may be created (path doe...
Definition: librpmDb.h:442
bool hasSubkeys() const
!<
Definition: PublicKey.h:323
Convenient building of std::string with boost::format.
Definition: String.h:251
std::string basename() const
Return the last component of this path.
Definition: Pathname.h:127
Provide a new empty temporary file and delete it when no longer needed.
Definition: TmpPath.h:127
void importZyppKeyRingTrustedKeys()
iterates through zypp keyring and import all non existant keys into rpm keyring
Definition: RpmDb.cc:1035
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:36
~RpmDb()
Destructor.
Definition: RpmDb.cc:252
bool backupPackage(const std::string &packageName)
create tar.gz of all changed files in a Package
Definition: RpmDb.cc:2277
#define ERR
Definition: Logger.h:66
CheckPackageResult checkPackage(const Pathname &path_r, CheckPackageDetail &detail_r)
Check signature of rpm file on disk (legacy version returning CHK_OK if file is unsigned, like &#39;rpm -K&#39;)
Definition: RpmDb.cc:1619
#define FILEFORBACKUPFILES
Definition: RpmDb.cc:57
Subclass to retrieve database content.
Definition: librpmDb.h:490
Temporarily connect a ReceiveReport then restore the previous one.
Definition: Callback.h:284
void importPubkey(const PublicKey &pubkey_r)
Import ascii armored public key in file pubkey_r.
Definition: RpmDb.cc:1047
bool hasDbV3ToV4() const
Whether dbV3ToV4 file exists.
Definition: librpmDb.h:474
bool hasPackage(const std::string &name_r) const
Return true if package is installed.
Definition: RpmDb.cc:1412
void systemKill()
Forcably kill the system process.
Definition: RpmDb.cc:1840
bool empty() const
Test for an empty path.
Definition: Pathname.h:113
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
Definition: Exception.h:400
void moveToHistory(TContainer &&msgc_r)
addHistory from string container types (oldest first) moving
Definition: Exception.h:234
void syncTrustedKeys(SyncTrustedKeyBits mode_r=SYNC_BOTH)
Sync trusted keys stored in rpm database and zypp trusted keyring.
Definition: RpmDb.cc:948
#define FAILIFNOTINITIALIZED
Definition: RpmDb.cc:220
std::string getline(std::istream &str)
Read one line from stream.
Definition: IOStream.cc:33
Store and operate on date (time_t).
Definition: Date.h:32
const std::string & execError() const
Some detail telling why the execution failed, if it failed.
Pathname _backuppath
/var/adm/backup
Definition: RpmDb.h:397
std::string version() const
Version.
Definition: Edition.cc:94
shared_ptr< RpmException > dbError() const
Return any database error.
Definition: librpmDb.cc:775
std::string form(const std::string &format_r) const
Return string representation according to format as localtime.
Definition: Date.h:112
std::string asString() const
Definition: IdStringType.h:106
int exit_code
The exit code of the rpm process, or -1 if not yet known.
Definition: RpmDb.h:388
std::list< PublicKey > pubkeys() const
Return the long ids of all installed public keys.
Definition: RpmDb.cc:1235
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
void dbsi_set(DbStateInfoBits &val_r, const unsigned &bits_r) const
Definition: RpmDb.h:75
int unlink(const Pathname &path)
Like &#39;unlink&#39;.
Definition: PathInfo.cc:653
std::string gpgPubkeyVersion() const
Definition: PublicKey.cc:535
SyncTrustedKeyBits
Sync mode for syncTrustedKeys.
Definition: RpmDb.h:327
bool systemReadLine(std::string &line)
Read a line from the general rpm query.
Definition: RpmDb.cc:1743
const std::string & asString() const
String representation.
Definition: Pathname.h:90
#define WARNINGMAILPATH
Definition: RpmDb.cc:56
int rename(const Pathname &oldpath, const Pathname &newpath)
Like &#39;rename&#39;.
Definition: PathInfo.cc:695
int systemStatus()
Return the exit status of the general rpm process, closing the connection if not already done...
Definition: RpmDb.cc:1817
std::set< Edition > pubkeyEditions() const
Return the edition of all installed public keys.
Definition: RpmDb.cc:1273
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:281
bool findByName(const std::string &name_r)
Reset to iterate all packages with a certain name.
Definition: librpmDb.cc:859
std::string asUserHistory() const
A single (multiline) string composed of asUserString and historyAsString.
Definition: Exception.cc:91
int recursive_rmdir(const Pathname &path)
Like &#39;rm -r DIR&#39;.
Definition: PathInfo.cc:413
std::string release() const
Release.
Definition: Edition.cc:110
#define WAR
Definition: Logger.h:65
Detailed rpm signature check log messages A single multiline message if CHK_OK.
Definition: RpmDb.h:444
virtual std::ostream & dumpOn(std::ostream &str) const
Dump debug info.
Definition: RpmDb.cc:284
bool startsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasPrefix
Definition: String.h:1069
Types and functions for filesystem operations.
Definition: Glob.cc:23
static unsigned dbRelease(bool force_r=false)
If there are no outstanding references to the database (e.g.
Definition: librpmDb.cc:289
static shared_ptr< KeyRingSignalReceiver > sKeyRingReceiver
Definition: RpmDb.cc:140
FILE * _f
Definition: RpmDb.cc:1503
ExternalProgram * process
The connection to the rpm process.
Definition: RpmDb.h:351
#define nullptr
Definition: Easy.h:54
Writing the zypp history fileReference counted signleton for writhing the zypp history file...
Definition: HistoryLog.h:55
void doRebuildDatabase(callback::SendReport< RebuildDBReport > &report)
Definition: RpmDb.cc:778
bool absolute() const
Test for an absolute path.
Definition: Pathname.h:115
#define _(MSG)
Definition: Gettext.h:29
bool findByFile(const std::string &file_r)
Reset to iterate all packages that own a certain file.
Definition: librpmDb.cc:815
std::string receiveLine()
Read one line from the input stream.
void closeDatabase()
Block further access to the rpm database and go back to uninitialized state.
Definition: RpmDb.cc:713
Stderr_Disposition
Define symbols for different policies on the handling of stderr.
DbStateInfoBits _dbStateInfo
Internal state info.
Definition: RpmDb.h:91
bool hasProvides(const std::string &tag_r) const
Return true if at least one package provides a certain tag.
Definition: RpmDb.cc:1370
bool dbsi_has(const DbStateInfoBits &val_r, const unsigned &bits_r) const
Definition: RpmDb.h:83
Just inherits Exception to separate media exceptions.
Definition: RpmException.h:37
static RpmHeader::constPtr readPackage(const Pathname &path, VERIFICATION verification=VERIFY)
Get an accessible packages data from disk.
Definition: RpmHeader.cc:208
std::string numstring(char n, int w=0)
Definition: String.h:288
export rpm trusted keys into zypp trusted keyring
Definition: RpmDb.h:330
#define OUTS(E, S)
SolvableIdType size_type
Definition: PoolMember.h:126
virtual void trustedKeyRemoved(const PublicKey &key)
Definition: RpmDb.cc:131
bool findPackage(const std::string &name_r)
Find package by name.
bool illegalArgs() const
Whether constructor arguments were illegal.
Definition: librpmDb.h:433
static void unblockAccess()
Allow access to rpmdb e.g.
Definition: librpmDb.cc:339
std::ostream & copy(std::istream &from_r, std::ostream &to_r)
Copy istream to ostream.
Definition: IOStream.h:50
void doInstallPackage(const Pathname &filename, RpmInstFlags flags, callback::SendReport< RpmInstallReport > &report)
Definition: RpmDb.cc:1974
int close()
Wait for the progamm to complete.
void removePubkey(const PublicKey &pubkey_r)
Remove a public key from the rpm database.
Definition: RpmDb.cc:1165
void processConfigFiles(const std::string &line, const std::string &name, const char *typemsg, const char *difffailmsg, const char *diffgenmsg)
handle rpm messages like "/etc/testrc saved as /etc/testrc.rpmorig"
Definition: RpmDb.cc:1847
int copy(const Pathname &file, const Pathname &dest)
Like &#39;cp file dest&#39;.
Definition: PathInfo.cc:773
bool _packagebackups
create package backups?
Definition: RpmDb.h:400
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition: Exception.h:396
std::string gpgPubkeyRelease() const
Definition: PublicKey.cc:538
Class representing one GPG Public Key (PublicKeyData + ASCII armored in a tempfile).
Definition: PublicKey.h:269
const PathInfo & dbV3() const
rpmV3 database (_dbDir/packages.rpm)
Definition: librpmDb.h:408
void doRemovePackage(const std::string &name_r, RpmInstFlags flags, callback::SendReport< RpmRemoveReport > &report)
Definition: RpmDb.cc:2162
Base class for Exception.
Definition: Exception.h:145
bool hasDbV4() const
Whether dbV4 file exists.
Definition: librpmDb.h:458
void setBackupPath(const Pathname &path)
set path where package backups are stored
Definition: RpmDb.cc:2404
const Pathname & root() const
Definition: RpmDb.h:151
bool hasConflicts(const std::string &tag_r) const
Return true if at least one package conflicts with a certain tag.
Definition: RpmDb.cc:1398
Pathname path() const
File containig the ASCII armored key.
Definition: PublicKey.cc:505
const Pathname & dbPath() const
Definition: RpmDb.h:159
const PathInfo & dbV4() const
rpmV4 database (_dbDir/Packages)
Definition: librpmDb.h:400
static Date now()
Return the current time.
Definition: Date.h:78
void convertV3toV4(const Pathname &v3db_r, const librpmDb::constPtr &v4db_r)
Definition: librpmDb.cv3.cc:39
callback::SendReport< DownloadProgressReport > * report
Definition: MediaCurl.cc:199
void initDatabase(Pathname root_r=Pathname(), Pathname dbPath_r=Pathname(), bool doRebuild_r=false)
Prepare access to the rpm database.
Definition: RpmDb.cc:315
std::string error_message
Error message from running rpm as external program.
Definition: RpmDb.h:394
std::string whoOwnsFile(const std::string &file_r) const
Return name of package owning file or empty string if no installed package owns file.
Definition: RpmDb.cc:1352
void removePackage(const std::string &name_r, RpmInstFlags flags=RPMINST_NONE)
remove rpm package
Definition: RpmDb.cc:2131
static bool globalInit()
Initialize lib librpm (read configfiles etc.).
Definition: librpmDb.cc:128
std::string * _cap
Definition: RpmDb.cc:1504
std::list< FileInfo > fileList(const std::string &name_r, const Edition &edition_r) const
return complete file list for installed package name_r (in FileInfo.filename) if edition_r != Edition...
Definition: RpmDb.cc:1297
std::string asString() const
Definition: PublicKey.cc:541
bool relative() const
Test for a relative path.
Definition: Pathname.h:117
bool hasFile(const std::string &file_r, const std::string &name_r="") const
Return true if at least one package owns a certain file (name_r empty) Return true if package name_r ...
Definition: RpmDb.cc:1326
void comment(const std::string &comment, bool timestamp=false)
Log a comment (even multiline).
Definition: HistoryLog.cc:188
bool findByConflicts(const std::string &tag_r)
Reset to iterate all packages that conflict with a certain tag.
Definition: librpmDb.cc:848
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:220
void setBlocking(bool mode)
Set the blocking mode of the input stream.
CheckPackageResult
checkPackage result
Definition: RpmDb.h:429
std::string stringPath(const Pathname &root_r, const Pathname &sub_r)
Definition: RpmDb.cc:178
static void removeV3(const Pathname &dbdir_r, bool v3backup_r)
Remove the rpm3 database in dbdir_r.
Definition: RpmDb.cc:624
bool queryChangedFiles(FileList &fileList, const std::string &packageName)
determine which files of an installed package have been modified.
Definition: RpmDb.cc:1631
pid_t getpid()
return pid
static void removeV4(const Pathname &dbdir_r, bool v3backup_r)
Remove the rpm4 database in dbdir_r and optionally any backup created on conversion.
Definition: RpmDb.cc:558
FILE * inputFile() const
Return the input stream.
std::string strerror(int errno_r)
Return string describing the error_r code.
Definition: String.cc:53
std::ostream & operator<<(std::ostream &str, const Glob &obj)
Definition: Glob.cc:53
intrusive_ptr< const librpmDb > constPtr
Definition: librpmDb.h:42
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
void run_rpm(const RpmArgVec &options, ExternalProgram::Stderr_Disposition stderr_disp=ExternalProgram::Stderr_To_Stdout)
Run rpm with the specified arguments and handle stderr.
Definition: RpmDb.cc:1697
void dbsi_clr(DbStateInfoBits &val_r, const unsigned &bits_r) const
Definition: RpmDb.h:79
bool initialized() const
Definition: RpmDb.h:167
void restat()
Restat all paths.
Definition: librpmDb.cc:529
TraitsType::constPtrType constPtr
Definition: Package.h:38
#define MAXRPMMESSAGELINES
Definition: RpmDb.cc:58
#define DBG
Definition: Logger.h:63
static const Edition noedition
Value representing noedition ("") This is in fact a valid Edition.
Definition: Edition.h:73
Pathname _dbPath
Directory that contains the rpmdb.
Definition: RpmDb.h:101
std::set< std::string > FileList
Definition: RpmDb.h:423
const PathInfo & dbDir() const
database directory (unset on illegal constructor arguments)
Definition: librpmDb.h:392
std::vector< const char * > RpmArgVec
Definition: RpmDb.h:353