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