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