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