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