- Fixed OnMediLocation to use safe defaults. Added setLocaltion and
[platform/upstream/libzypp.git] / zypp / target / rpm / RpmDb.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/rpm/RpmDb.cc
10  *
11 */
12 #include "librpm.h"
13
14 #include <cstdlib>
15 #include <cstdio>
16 #include <ctime>
17
18 #include <iostream>
19 #include <fstream>
20 #include <list>
21 #include <map>
22 #include <set>
23 #include <string>
24 #include <vector>
25 #include <algorithm>
26
27 #include "zypp/base/Logger.h"
28 #include "zypp/base/String.h"
29
30 #include "zypp/Date.h"
31 #include "zypp/Pathname.h"
32 #include "zypp/PathInfo.h"
33 #include "zypp/PublicKey.h"
34
35 #include "zypp/target/rpm/RpmDb.h"
36 #include "zypp/target/rpm/RpmCallbacks.h"
37
38 #include "zypp/target/CommitLog.h"
39 #include "zypp/target/rpm/librpmDb.h"
40 #include "zypp/target/rpm/RpmPackageImpl.h"
41 #include "zypp/target/rpm/RpmException.h"
42 #include "zypp/CapSet.h"
43 #include "zypp/CapFactory.h"
44 #include "zypp/KeyRing.h"
45 #include "zypp/ZYppFactory.h"
46 #include "zypp/TmpPath.h"
47
48 #ifndef _
49 #define _(X) X
50 #endif
51
52 using namespace std;
53 using namespace zypp::filesystem;
54
55 namespace zypp
56 {
57 namespace target
58 {
59 namespace rpm
60 {
61 namespace
62 {
63 const char* quoteInFilename_m = " \t";
64 inline string rpmQuoteFilename( const Pathname & path_r )
65 {
66   string path( path_r.asString() );
67   for ( string::size_type pos = path.find_first_of( quoteInFilename_m );
68         pos != string::npos;
69         pos = path.find_first_of( quoteInFilename_m, pos ) )
70   {
71     path.insert( pos, "\\" );
72     pos += 2; // skip '\\' and the quoted char.
73   }
74   return path;
75 }
76 }
77
78 struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
79 {
80   KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
81   {
82     connect();
83   }
84
85   ~KeyRingSignalReceiver()
86   {
87     disconnect();
88   }
89
90   virtual void trustedKeyAdded( const PublicKey &key )
91   {
92     MIL << "trusted key added to zypp Keyring. Importing" << endl;
93     // now import the key in rpm
94     try
95     {
96       _rpmdb.importPubkey( key );
97     }
98     catch (RpmException &e)
99     {
100       ERR << "Could not import key " << key.id() << " (" << key.name() << " from " << key.path() << " in rpm database" << endl;
101     }
102   }
103
104   virtual void trustedKeyRemoved( const PublicKey &key  )
105   {}
106
107   RpmDb &_rpmdb;
108 };
109
110 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
111
112 unsigned diffFiles(const string file1, const string file2, string& out, int maxlines)
113 {
114   const char* argv[] =
115     {
116       "diff",
117       "-u",
118       file1.c_str(),
119       file2.c_str(),
120       NULL
121     };
122   ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
123
124   //if(!prog)
125   //return 2;
126
127   string line;
128   int count = 0;
129   for (line = prog.receiveLine(), count=0;
130        !line.empty();
131        line = prog.receiveLine(), count++ )
132   {
133     if (maxlines<0?true:count<maxlines)
134       out+=line;
135   }
136
137   return prog.close();
138 }
139
140
141
142 /******************************************************************
143  **
144  **
145  **     FUNCTION NAME : stringPath
146  **     FUNCTION TYPE : inline string
147 */
148 inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
149 {
150   return librpmDb::stringPath( root_r, sub_r );
151 }
152
153 /******************************************************************
154  **
155  **
156  **     FUNCTION NAME : operator<<
157  **     FUNCTION TYPE : ostream &
158 */
159 ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
160 {
161   if ( obj == RpmDb::DbSI_NO_INIT )
162   {
163     str << "NO_INIT";
164   }
165   else
166   {
167 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
168     str << "V4(";
169     ENUM_OUT( DbSI_HAVE_V4,     'X' );
170     ENUM_OUT( DbSI_MADE_V4,     'c' );
171     ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
172     str << ")V3(";
173     ENUM_OUT( DbSI_HAVE_V3,     'X' );
174     ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
175     ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
176     str << ")";
177 #undef ENUM_OUT
178   }
179   return str;
180 }
181
182 ///////////////////////////////////////////////////////////////////
183 //      CLASS NAME : RpmDbPtr
184 //      CLASS NAME : RpmDbconstPtr
185 ///////////////////////////////////////////////////////////////////
186
187 #define WARNINGMAILPATH "/var/log/YaST2/"
188 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
189
190 ///////////////////////////////////////////////////////////////////
191 //
192 //      CLASS NAME : RpmDb::Packages
193 /**
194  * Helper class for RpmDb::getPackages() to build the
195  * list<Package::Ptr> returned. We have to assert, that there
196  * is a unique entry for every string.
197  *
198  * In the first step we build the _list list which contains all
199  * packages (even those which are contained in multiple versions).
200  *
201  * At the end buildIndex() is called to build the _index is created
202  * and points to the last installed versions of all packages.
203  * Operations changing the rpmdb
204  * content (install/remove package) should set _valid to false. The
205  * next call to RpmDb::getPackages() will then reread the the rpmdb.
206  *
207  * Note that outside RpmDb::getPackages() _list and _index are always
208  * in sync. So you may use lookup(PkgName) to retrieve a specific
209  * Package::Ptr.
210  **/
211 class RpmDb::Packages
212 {
213 public:
214   list<Package::Ptr>        _list;
215   map<string,Package::Ptr> _index;
216   bool                      _valid;
217   Packages() : _valid( false )
218   {}
219   void clear()
220   {
221     _list.clear();
222     _index.clear();
223     _valid = false;
224   }
225   Package::Ptr lookup( const string & name_r ) const
226   {
227     map<string,Package::Ptr>::const_iterator got = _index.find( name_r );
228     if ( got != _index.end() )
229       return got->second;
230     return Package::Ptr();
231   }
232   void buildIndex()
233   {
234     _index.clear();
235     for ( list<Package::Ptr>::iterator iter = _list.begin();
236           iter != _list.end(); ++iter )
237     {
238       string name = (*iter)->name();
239       Package::Ptr & nptr = _index[name]; // be shure to get a reference!
240
241       if ( nptr )
242       {
243         WAR << "Multiple entries for package '" << name << "' in rpmdb" << endl;
244         if ( nptr->installtime() > (*iter)->installtime() )
245           continue;
246         else
247           nptr = *iter;
248       }
249       else
250       {
251         nptr = *iter;
252       }
253     }
254     _valid = true;
255   }
256 };
257
258 ///////////////////////////////////////////////////////////////////
259
260 ///////////////////////////////////////////////////////////////////
261 //
262 //      CLASS NAME : RpmDb
263 //
264 ///////////////////////////////////////////////////////////////////
265
266 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
267
268 ///////////////////////////////////////////////////////////////////
269
270 ///////////////////////////////////////////////////////////////////
271 //
272 //
273 //      METHOD NAME : RpmDb::RpmDb
274 //      METHOD TYPE : Constructor
275 //
276 RpmDb::RpmDb()
277     : _dbStateInfo( DbSI_NO_INIT )
278     , _packages( * new Packages ) // delete in destructor
279 #warning Check for obsolete memebers
280     , _backuppath ("/var/adm/backup")
281     , _packagebackups(false)
282     , _warndirexists(false)
283 {
284   process = 0;
285   exit_code = -1;
286
287   // Some rpm versions are patched not to abort installation if
288   // symlink creation failed.
289   setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
290   sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
291 }
292
293 ///////////////////////////////////////////////////////////////////
294 //
295 //
296 //      METHOD NAME : RpmDb::~RpmDb
297 //      METHOD TYPE : Destructor
298 //
299 RpmDb::~RpmDb()
300 {
301   MIL << "~RpmDb()" << endl;
302   closeDatabase();
303
304   delete process;
305   delete &_packages;
306   MIL  << "~RpmDb() end" << endl;
307   sKeyRingReceiver.reset();
308 }
309
310 Date RpmDb::timestamp() const
311 {
312   Date ts_rpm;
313
314   Pathname db_path;
315   if ( dbPath().empty() )
316     db_path = "/var/lib/rpm";
317   else
318     db_path = dbPath();
319
320   PathInfo rpmdb_info(root() + db_path + "/Packages");
321
322   if ( rpmdb_info.isExist() )
323     return rpmdb_info.mtime();
324   else
325     return Date::now();
326 }
327 ///////////////////////////////////////////////////////////////////
328 //
329 //
330 //      METHOD NAME : RpmDb::dumpOn
331 //      METHOD TYPE : ostream &
332 //
333 ostream & RpmDb::dumpOn( ostream & str ) const
334 {
335   str << "RpmDb[";
336
337   if ( _dbStateInfo == DbSI_NO_INIT )
338   {
339     str << "NO_INIT";
340   }
341   else
342   {
343 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
344     str << "V4(";
345     ENUM_OUT( DbSI_HAVE_V4,     'X' );
346     ENUM_OUT( DbSI_MADE_V4,     'c' );
347     ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
348     str << ")V3(";
349     ENUM_OUT( DbSI_HAVE_V3,     'X' );
350     ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
351     ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
352     str << "): " << stringPath( _root, _dbPath );
353 #undef ENUM_OUT
354   }
355   return str << "]";
356 }
357
358 ///////////////////////////////////////////////////////////////////
359 //
360 //
361 //      METHOD NAME : RpmDb::initDatabase
362 //      METHOD TYPE : PMError
363 //
364 void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r )
365 {
366   ///////////////////////////////////////////////////////////////////
367   // Check arguments
368   ///////////////////////////////////////////////////////////////////
369   if ( root_r.empty() )
370     root_r = "/";
371
372   if ( dbPath_r.empty() )
373     dbPath_r = "/var/lib/rpm";
374
375   if ( ! (root_r.absolute() && dbPath_r.absolute()) )
376   {
377     ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
378     ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
379   }
380
381   MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r ) << endl;
382
383   ///////////////////////////////////////////////////////////////////
384   // Check whether already initialized
385   ///////////////////////////////////////////////////////////////////
386   if ( initialized() )
387   {
388     if ( root_r == _root && dbPath_r == _dbPath )
389     {
390       return;
391     }
392     else
393     {
394       ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
395     }
396   }
397
398   ///////////////////////////////////////////////////////////////////
399   // init database
400   ///////////////////////////////////////////////////////////////////
401   librpmDb::unblockAccess();
402   DbStateInfoBits info = DbSI_NO_INIT;
403   try
404   {
405     internal_initDatabase( root_r, dbPath_r, info );
406   }
407   catch (const RpmException & excpt_r)
408   {
409     ZYPP_CAUGHT(excpt_r);
410     librpmDb::blockAccess();
411     ERR << "Cleanup on error: state " << info << endl;
412
413     if ( dbsi_has( info, DbSI_MADE_V4 ) )
414     {
415       // remove the newly created rpm4 database and
416       // any backup created on conversion.
417       removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
418     }
419     ZYPP_RETHROW(excpt_r);
420   }
421   if ( dbsi_has( info, DbSI_HAVE_V3 ) )
422   {
423     if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
424     {
425       // Move obsolete rpm3 database beside.
426       MIL << "Cleanup: state " << info << endl;
427       removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
428       dbsi_clr( info, DbSI_HAVE_V3 );
429     }
430     else
431     {
432       // Performing an update: Keep the original rpm3 database
433       // and wait if the rpm4 database gets modified by installing
434       // or removing packages. Cleanup in modifyDatabase or closeDatabase.
435       MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
436     }
437   }
438 #warning CHECK: notify root about conversion backup.
439
440   _root   = root_r;
441   _dbPath = dbPath_r;
442   _dbStateInfo = info;
443
444 #warning Add rebuild database once have the info about context
445 #if 0
446   if ( ! ( Y2PM::runningFromSystem() ) )
447   {
448     if (      dbsi_has( info, DbSI_HAVE_V4 )
449               && ! dbsi_has( info, DbSI_MADE_V4 ) )
450     {
451       err = rebuildDatabase();
452     }
453   }
454 #endif
455
456   MIL << "Syncronizing keys with zypp keyring" << endl;
457   // we do this one by one now.
458   //importZyppKeyRingTrustedKeys();
459   exportTrustedKeysInZyppKeyRing();
460
461   // Close the database in case any write acces (create/convert)
462   // happened during init. This should drop any lock acquired
463   // by librpm. On demand it will be reopened readonly and should
464   // not hold any lock.
465   librpmDb::dbRelease( true );
466
467   MIL << "InitDatabase: " << *this << endl;
468 }
469
470 ///////////////////////////////////////////////////////////////////
471 //
472 //
473 //      METHOD NAME : RpmDb::internal_initDatabase
474 //      METHOD TYPE : PMError
475 //
476 void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
477                                    DbStateInfoBits & info_r )
478 {
479   info_r = DbSI_NO_INIT;
480
481   ///////////////////////////////////////////////////////////////////
482   // Get info about the desired database dir
483   ///////////////////////////////////////////////////////////////////
484   librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
485
486   if ( dbInfo.illegalArgs() )
487   {
488     // should not happen (checked in initDatabase)
489     ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
490   }
491   if ( ! dbInfo.usableArgs() )
492   {
493     ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
494     ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
495   }
496
497   if ( dbInfo.hasDbV4() )
498   {
499     dbsi_set( info_r, DbSI_HAVE_V4 );
500     MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
501   }
502   else
503   {
504     MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
505   }
506
507   if ( dbInfo.hasDbV3() )
508   {
509     dbsi_set( info_r, DbSI_HAVE_V3 );
510   }
511   if ( dbInfo.hasDbV3ToV4() )
512   {
513     dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
514   }
515
516   DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
517   librpmDb::dumpState( DBG ) << endl;
518
519   ///////////////////////////////////////////////////////////////////
520   // Access database, create if needed
521   ///////////////////////////////////////////////////////////////////
522
523   // creates dbdir and empty rpm4 database if not present
524   librpmDb::dbAccess( root_r, dbPath_r );
525
526   if ( ! dbInfo.hasDbV4() )
527   {
528     dbInfo.restat();
529     if ( dbInfo.hasDbV4() )
530     {
531       dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
532     }
533   }
534
535   DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
536   librpmDb::dumpState( DBG ) << endl;
537
538   ///////////////////////////////////////////////////////////////////
539   // Check whether to convert something. Create backup but do
540   // not remove anything here
541   ///////////////////////////////////////////////////////////////////
542   librpmDb::constPtr dbptr;
543   librpmDb::dbAccess( dbptr );
544   bool dbEmpty = dbptr->empty();
545   if ( dbEmpty )
546   {
547     MIL << "Empty rpm4 database "  << dbInfo.dbV4() << endl;
548   }
549
550   if ( dbInfo.hasDbV3() )
551   {
552     MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
553
554     if ( dbEmpty )
555     {
556       extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
557       convertV3toV4( dbInfo.dbV3().path(), dbptr );
558
559       // create a backup copy
560       int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
561       if ( res )
562       {
563         WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
564       }
565       else
566       {
567         dbInfo.restat();
568         if ( dbInfo.hasDbV3ToV4() )
569         {
570           MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
571           dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
572         }
573       }
574
575     }
576     else
577     {
578
579       WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
580 #warning EXCEPTION: nonempty rpm4 and rpm3 database found.
581       //ConvertDbReport::Send report( RpmDbCallbacks::convertDbReport );
582       //report->start( dbInfo.dbV3().path() );
583       //report->stop( some error );
584
585       // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
586       dbsi_set( info_r, DbSI_MODIFIED_V4 );
587
588     }
589
590     DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
591     librpmDb::dumpState( DBG ) << endl;
592   }
593
594   if ( dbInfo.hasDbV3ToV4() )
595   {
596     MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
597   }
598 }
599
600 ///////////////////////////////////////////////////////////////////
601 //
602 //
603 //      METHOD NAME : RpmDb::removeV4
604 //      METHOD TYPE : void
605 //
606 void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
607 {
608   const char * v3backup = "packages.rpm3";
609   const char * master = "Packages";
610   const char * index[] =
611     {
612       "Basenames",
613       "Conflictname",
614       "Depends",
615       "Dirnames",
616       "Filemd5s",
617       "Group",
618       "Installtid",
619       "Name",
620       "Providename",
621       "Provideversion",
622       "Pubkeys",
623       "Requirename",
624       "Requireversion",
625       "Sha1header",
626       "Sigmd5",
627       "Triggername",
628       // last entry!
629       NULL
630     };
631
632   PathInfo pi( dbdir_r );
633   if ( ! pi.isDir() )
634   {
635     ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
636     return;
637   }
638
639   for ( const char ** f = index; *f; ++f )
640   {
641     pi( dbdir_r + *f );
642     if ( pi.isFile() )
643     {
644       filesystem::unlink( pi.path() );
645     }
646   }
647
648   pi( dbdir_r + master );
649   if ( pi.isFile() )
650   {
651     MIL << "Removing rpm4 database " << pi << endl;
652     filesystem::unlink( pi.path() );
653   }
654
655   if ( v3backup_r )
656   {
657     pi( dbdir_r + v3backup );
658     if ( pi.isFile() )
659     {
660       MIL << "Removing converted rpm3 database backup " << pi << endl;
661       filesystem::unlink( pi.path() );
662     }
663   }
664 }
665
666 ///////////////////////////////////////////////////////////////////
667 //
668 //
669 //      METHOD NAME : RpmDb::removeV3
670 //      METHOD TYPE : void
671 //
672 void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
673 {
674   const char * master = "packages.rpm";
675   const char * index[] =
676     {
677       "conflictsindex.rpm",
678       "fileindex.rpm",
679       "groupindex.rpm",
680       "nameindex.rpm",
681       "providesindex.rpm",
682       "requiredby.rpm",
683       "triggerindex.rpm",
684       // last entry!
685       NULL
686     };
687
688   PathInfo pi( dbdir_r );
689   if ( ! pi.isDir() )
690   {
691     ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
692     return;
693   }
694
695   for ( const char ** f = index; *f; ++f )
696   {
697     pi( dbdir_r + *f );
698     if ( pi.isFile() )
699     {
700       filesystem::unlink( pi.path() );
701     }
702   }
703
704 #warning CHECK: compare vs existing v3 backup. notify root
705   pi( dbdir_r + master );
706   if ( pi.isFile() )
707   {
708     Pathname m( pi.path() );
709     if ( v3backup_r )
710     {
711       // backup was already created
712       filesystem::unlink( m );
713       Pathname b( m.extend( "3" ) );
714       pi( b ); // stat backup
715     }
716     else
717     {
718       Pathname b( m.extend( ".deleted" ) );
719       pi( b );
720       if ( pi.isFile() )
721       {
722         // rempve existing backup
723         filesystem::unlink( b );
724       }
725       filesystem::rename( m, b );
726       pi( b ); // stat backup
727     }
728     MIL << "(Re)moved rpm3 database to " << pi << endl;
729   }
730 }
731
732 ///////////////////////////////////////////////////////////////////
733 //
734 //
735 //      METHOD NAME : RpmDb::modifyDatabase
736 //      METHOD TYPE : void
737 //
738 void RpmDb::modifyDatabase()
739 {
740   if ( ! initialized() )
741     return;
742
743   // tag database as modified
744   dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
745
746   // Move outdated rpm3 database beside.
747   if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
748   {
749     MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
750     removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
751     dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
752   }
753
754   // invalidate Packages list
755   _packages._valid = false;
756 }
757
758 ///////////////////////////////////////////////////////////////////
759 //
760 //
761 //      METHOD NAME : RpmDb::closeDatabase
762 //      METHOD TYPE : PMError
763 //
764 void RpmDb::closeDatabase()
765 {
766   if ( ! initialized() )
767   {
768     return;
769   }
770
771   MIL << "Calling closeDatabase: " << *this << endl;
772
773   ///////////////////////////////////////////////////////////////////
774   // Block further database access
775   ///////////////////////////////////////////////////////////////////
776   _packages.clear();
777   librpmDb::blockAccess();
778
779   ///////////////////////////////////////////////////////////////////
780   // Check fate if old version database still present
781   ///////////////////////////////////////////////////////////////////
782   if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
783   {
784     MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
785     if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) )
786     {
787       // Move outdated rpm3 database beside.
788       removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 )  );
789     }
790     else
791     {
792       // Remove unmodified rpm4 database
793       removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
794     }
795   }
796
797   ///////////////////////////////////////////////////////////////////
798   // Uninit
799   ///////////////////////////////////////////////////////////////////
800   _root = _dbPath = Pathname();
801   _dbStateInfo = DbSI_NO_INIT;
802
803   MIL << "closeDatabase: " << *this << endl;
804 }
805
806 ///////////////////////////////////////////////////////////////////
807 //
808 //
809 //      METHOD NAME : RpmDb::rebuildDatabase
810 //      METHOD TYPE : PMError
811 //
812 void RpmDb::rebuildDatabase()
813 {
814   callback::SendReport<RebuildDBReport> report;
815
816   report->start( root() + dbPath() );
817
818   try
819   {
820     doRebuildDatabase(report);
821   }
822   catch (RpmException & excpt_r)
823   {
824     report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserString());
825     ZYPP_RETHROW(excpt_r);
826   }
827   report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
828 }
829
830 void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
831 {
832   FAILIFNOTINITIALIZED;
833
834   MIL << "RpmDb::rebuildDatabase" << *this << endl;
835   // FIXME  Timecount _t( "RpmDb::rebuildDatabase" );
836
837   PathInfo dbMaster( root() + dbPath() + "Packages" );
838   PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
839
840   // run rpm
841   RpmArgVec opts;
842   opts.push_back("--rebuilddb");
843   opts.push_back("-vv");
844
845   // don't call modifyDatabase because it would remove the old
846   // rpm3 database, if the current database is a temporary one.
847   // But do invalidate packages list.
848   _packages._valid = false;
849   run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
850
851   // progress report: watch this file growing
852   PathInfo newMaster( root()
853                       + dbPath().extend( str::form( "rebuilddb.%d",
854                                                     process?process->getpid():0) )
855                       + "Packages" );
856
857   string       line;
858   string       errmsg;
859
860   while ( systemReadLine( line ) )
861   {
862     if ( newMaster() )
863     { // file is removed at the end of rebuild.
864       // current size should be upper limit for new db
865       report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath());
866     }
867
868     if ( line.compare( 0, 2, "D:" ) )
869     {
870       errmsg += line + '\n';
871       //      report.notify( line );
872       WAR << line << endl;
873     }
874   }
875
876   int rpm_status = systemStatus();
877
878   if ( rpm_status != 0 )
879   {
880     ZYPP_THROW(RpmSubprocessException(string("rpm failed with message: ") + errmsg));
881   }
882   else
883   {
884     report->progress( 100, root() + dbPath() ); // 100%
885   }
886 }
887
888 void RpmDb::exportTrustedKeysInZyppKeyRing()
889 {
890   MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
891
892   set<Edition> rpm_keys = pubkeyEditions();
893
894   list<PublicKey> zypp_keys;
895   zypp_keys = getZYpp()->keyRing()->trustedPublicKeys();
896
897   for ( set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
898   {
899     // search the zypp key into the rpm keys
900     // long id is edition version + release
901     string id = str::toUpper( (*it).version() + (*it).release());
902     list<PublicKey>::iterator ik = find( zypp_keys.begin(), zypp_keys.end(), id);
903     if ( ik != zypp_keys.end() )
904     {
905       MIL << "Key " << (*it) << " is already in zypp database." << endl;
906     }
907     else
908     {
909       // we export the rpm key into a file
910       RpmHeader::constPtr result = new RpmHeader();
911       getData( string("gpg-pubkey"), *it, result );
912       TmpFile file(getZYpp()->tmpPath());
913       ofstream os;
914       try
915       {
916         os.open(file.path().asString().c_str());
917         // dump rpm key into the tmp file
918         os << result->tag_description();
919         //MIL << "-----------------------------------------------" << endl;
920         //MIL << result->tag_description() <<endl;
921         //MIL << "-----------------------------------------------" << endl;
922         os.close();
923       }
924       catch (exception &e)
925       {
926         ERR << "Could not dump key " << (*it) << " in tmp file " << file.path() << endl;
927         // just ignore the key
928       }
929
930       // now import the key in zypp
931       try
932       {
933         getZYpp()->keyRing()->importKey( file.path(), true /*trusted*/);
934         MIL << "Trusted key " << (*it) << " imported in zypp keyring." << endl;
935       }
936       catch (Exception &e)
937       {
938         ERR << "Could not import key " << (*it) << " in zypp keyring" << endl;
939       }
940     }
941   }
942 }
943
944 ///////////////////////////////////////////////////////////////////
945 //
946 //
947 //      METHOD NAME : RpmDb::importPubkey
948 //      METHOD TYPE : PMError
949 //
950 void RpmDb::importPubkey( const PublicKey & pubkey_r )
951 {
952   FAILIFNOTINITIALIZED;
953
954   // check if the key is already in the rpm database and just
955   // return if it does.
956   set<Edition> rpm_keys = pubkeyEditions();
957   for ( set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
958   {
959     string id = str::toUpper( (*it).version() );
960     string keyshortid = pubkey_r.id().substr(8,8);
961     MIL << "Comparing '" << id << "' to '" << keyshortid << "'" << endl;
962     if ( id == keyshortid )
963     {
964       // they match id
965       // FIXME id is not sufficient?
966       MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring." << endl;
967       return;
968     }
969   }
970   // key does not exists, lets import it
971
972   RpmArgVec opts;
973   opts.push_back ( "--import" );
974   opts.push_back ( "--" );
975   opts.push_back ( pubkey_r.path().asString().c_str() );
976
977   // don't call modifyDatabase because it would remove the old
978   // rpm3 database, if the current database is a temporary one.
979   // But do invalidate packages list.
980   _packages._valid = false;
981   run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
982
983   string line;
984   while ( systemReadLine( line ) )
985   {
986     if ( line.substr( 0, 6 ) == "error:" )
987     {
988       WAR << line << endl;
989     }
990     else
991     {
992       DBG << line << endl;
993     }
994   }
995
996   int rpm_status = systemStatus();
997
998   if ( rpm_status != 0 )
999   {
1000     ZYPP_THROW(RpmSubprocessException(string("Failed to import public key from file ") + pubkey_r.asString() + string(": rpm returned  ") + str::numstring(rpm_status)));
1001   }
1002   else
1003   {
1004     MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
1005   }
1006 }
1007
1008 ///////////////////////////////////////////////////////////////////
1009 //
1010 //
1011 //      METHOD NAME : RpmDb::pubkeys
1012 //      METHOD TYPE : set<Edition>
1013 //
1014 list<PublicKey> RpmDb::pubkeys() const
1015 {
1016   list<PublicKey> ret;
1017
1018   librpmDb::db_const_iterator it;
1019   for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
1020   {
1021     Edition edition = it->tag_edition();
1022     if (edition != Edition::noedition)
1023     {
1024       // we export the rpm key into a file
1025       RpmHeader::constPtr result = new RpmHeader();
1026       getData( string("gpg-pubkey"), edition, result );
1027       TmpFile file(getZYpp()->tmpPath());
1028       ofstream os;
1029       try
1030       {
1031         os.open(file.path().asString().c_str());
1032         // dump rpm key into the tmp file
1033         os << result->tag_description();
1034         //MIL << "-----------------------------------------------" << endl;
1035         //MIL << result->tag_description() <<endl;
1036         //MIL << "-----------------------------------------------" << endl;
1037         os.close();
1038         // read the public key from the dumped file
1039         PublicKey key(file.path());
1040         ret.push_back(key);
1041       }
1042       catch (exception &e)
1043       {
1044         ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
1045         // just ignore the key
1046       }
1047     }
1048   }
1049   return ret;
1050 }
1051
1052 set<Edition> RpmDb::pubkeyEditions() const
1053   {
1054     set<Edition> ret;
1055
1056     librpmDb::db_const_iterator it;
1057     for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
1058     {
1059       Edition edition = it->tag_edition();
1060       if (edition != Edition::noedition)
1061         ret.insert( edition );
1062     }
1063     return ret;
1064   }
1065
1066 ///////////////////////////////////////////////////////////////////
1067 //
1068 //
1069 //      METHOD NAME : RpmDb::packagesValid
1070 //      METHOD TYPE : bool
1071 //
1072 bool RpmDb::packagesValid() const
1073 {
1074   return( _packages._valid || ! initialized() );
1075 }
1076
1077 ///////////////////////////////////////////////////////////////////
1078 //
1079 //
1080 //      METHOD NAME : RpmDb::getPackages
1081 //      METHOD TYPE : const list<Package::Ptr> &
1082 //
1083 //      DESCRIPTION :
1084 //
1085 const list<Package::Ptr> & RpmDb::getPackages()
1086 {
1087   callback::SendReport<ScanDBReport> report;
1088
1089   report->start ();
1090
1091   try
1092   {
1093     const list<Package::Ptr> & ret = doGetPackages(report);
1094     report->finish(ScanDBReport::NO_ERROR, "");
1095     return ret;
1096   }
1097   catch (RpmException & excpt_r)
1098   {
1099     report->finish(ScanDBReport::FAILED, excpt_r.asUserString ());
1100     ZYPP_RETHROW(excpt_r);
1101   }
1102 #warning fixme
1103   static const list<Package::Ptr> empty_list;
1104   return empty_list;
1105 }
1106
1107 inline static void insertCaps( CapSet &capset, capability::CapabilityImplPtrSet ptrset, CapFactory &factory )
1108 {
1109   for ( capability::CapabilityImplPtrSet::const_iterator it = ptrset.begin();
1110         it != ptrset.end();
1111         ++it )
1112   {
1113     capset.insert( factory.fromImpl(*it) );
1114   }
1115 }
1116
1117 //
1118 // make Package::Ptr from RpmHeader
1119 // return NULL on error
1120 //
1121 Package::Ptr RpmDb::makePackageFromHeader( const RpmHeader::constPtr header,
1122                                            set<string> * filerequires,
1123                                            const Pathname & location, Repository repo )
1124 {
1125   if ( ! header )
1126     return 0;
1127
1128   if ( header->isSrc() )
1129   {
1130     WAR << "Can't make Package from SourcePackage header" << endl;
1131     return 0;
1132   }
1133
1134   Package::Ptr pptr;
1135
1136   string name = header->tag_name();
1137
1138   // create dataprovider
1139   detail::ResImplTraits<RPMPackageImpl>::Ptr impl( new RPMPackageImpl( header ) );
1140
1141   impl->setRepository( repo );
1142   if (!location.empty())
1143     impl->setLocation( OnMediaLocation(location,1) );
1144
1145   Edition edition;
1146   try
1147   {
1148     edition = Edition( header->tag_version(),
1149                        header->tag_release(),
1150                        header->tag_epoch());
1151   }
1152   catch (Exception & excpt_r)
1153   {
1154     ZYPP_CAUGHT( excpt_r );
1155     WAR << "Package " << name << " has bad edition '"
1156     << (header->tag_epoch().empty()?"":(header->tag_epoch()+":"))
1157     << header->tag_version()
1158     << (header->tag_release().empty()?"":(string("-") + header->tag_release())) << "'";
1159     return pptr;
1160   }
1161
1162   Arch arch;
1163   try
1164   {
1165     arch = Arch( header->tag_arch() );
1166   }
1167   catch (Exception & excpt_r)
1168   {
1169     ZYPP_CAUGHT( excpt_r );
1170     WAR << "Package " << name << " has bad architecture '" << header->tag_arch() << "'";
1171     return pptr;
1172   }
1173
1174   // Collect basic Resolvable data
1175   NVRAD dataCollect( header->tag_name(),
1176                      edition,
1177                      arch );
1178
1179   list<string> filenames = impl->filenames();
1180   CapFactory capfactory;
1181   insertCaps( dataCollect[Dep::PROVIDES], header->tag_provides( filerequires ), capfactory );
1182
1183   static str::smatch what;
1184   static const str::regex filenameRegex( "/(s?bin|lib(64)?|etc)/|^/usr/(games/|share/(dict/words|magic\\.mime)$)|^/opt/gnome/games/",
1185                                          str::regex::optimize|str::regex::nosubs );
1186
1187   for (list<string>::const_iterator filename = filenames.begin();
1188        filename != filenames.end();
1189        ++filename)
1190   {
1191     if ( str::regex_search( filename->begin(), filename->end(), what, filenameRegex ) )
1192     {
1193       try
1194       {
1195         dataCollect[Dep::PROVIDES].insert(capfactory.fromImpl(capability::buildFile(ResTraits<Package>::kind, *filename) ));
1196       }
1197       catch (Exception & excpt_r)
1198       {
1199         ZYPP_CAUGHT( excpt_r );
1200         WAR << "Ignoring invalid capability: " << *filename << endl;
1201       }
1202     }
1203   }
1204
1205   insertCaps( dataCollect[Dep::REQUIRES], header->tag_requires( filerequires ), capfactory );
1206   insertCaps( dataCollect[Dep::PREREQUIRES], header->tag_prerequires( filerequires ), capfactory );
1207   insertCaps( dataCollect[Dep::CONFLICTS], header->tag_conflicts( filerequires ), capfactory );
1208   insertCaps( dataCollect[Dep::OBSOLETES], header->tag_obsoletes( filerequires ), capfactory );
1209   insertCaps( dataCollect[Dep::ENHANCES], header->tag_enhances( filerequires ), capfactory );
1210   insertCaps( dataCollect[Dep::SUPPLEMENTS], header->tag_supplements( filerequires ), capfactory );
1211
1212   try
1213   {
1214     // create package from dataprovider
1215     pptr = detail::makeResolvableFromImpl( dataCollect, impl );
1216   }
1217   catch (Exception & excpt_r)
1218   {
1219     ZYPP_CAUGHT( excpt_r );
1220     ERR << "Can't create Package::Ptr" << endl;
1221   }
1222
1223   return pptr;
1224 }
1225
1226 const list<Package::Ptr> & RpmDb::doGetPackages(callback::SendReport<ScanDBReport> & report)
1227 {
1228   if ( packagesValid() )
1229   {
1230     return _packages._list;
1231   }
1232
1233   _packages.clear();
1234
1235   ///////////////////////////////////////////////////////////////////
1236   // Collect package data.
1237   ///////////////////////////////////////////////////////////////////
1238   unsigned expect = 0;
1239   librpmDb::constPtr dbptr;
1240   librpmDb::dbAccess( dbptr );
1241   expect = dbptr->size();
1242   DBG << "Expecting " << expect << " packages" << endl;
1243
1244   librpmDb::db_const_iterator iter;
1245   unsigned current = 0;
1246   CapFactory _f;
1247   Pathname location;
1248
1249   for ( iter.findAll(); *iter; ++iter, ++current, report->progress( (100*current)/expect))
1250   {
1251
1252     string name = iter->tag_name();
1253     if ( name == string( "gpg-pubkey" ) )
1254     {
1255       DBG << "Ignoring pseudo package " << name << endl;
1256       // pseudo package filtered, as we can't handle multiple instances
1257       // of 'gpg-pubkey-VERS-REL'.
1258       continue;
1259     }
1260
1261     Package::Ptr pptr = makePackageFromHeader( *iter, &_filerequires, location, Repository() );
1262     if ( ! pptr )
1263     {
1264       WAR << "Failed to make package from database header '" << name << "'" << endl;
1265       continue;
1266     }
1267
1268     _packages._list.push_back( pptr );
1269   }
1270   _packages.buildIndex();
1271   DBG << "Found installed packages: " << _packages._list.size() << endl;
1272
1273   ///////////////////////////////////////////////////////////////////
1274   // Evaluate filerequires collected so far
1275   ///////////////////////////////////////////////////////////////////
1276   for ( set<string>::iterator it = _filerequires.begin(); it != _filerequires.end(); ++it )
1277     {
1278
1279       for ( iter.findByFile( *it ); *iter; ++iter )
1280       {
1281         Package::Ptr pptr = _packages.lookup( iter->tag_name() );
1282         if ( !pptr )
1283         {
1284           WAR << "rpmdb.findByFile returned unknown package " << *iter << endl;
1285           continue;
1286         }
1287         pptr->injectProvides(_f.parse(ResTraits<Package>::kind, *it));
1288       }
1289
1290     }
1291
1292   ///////////////////////////////////////////////////////////////////
1293   // Build final packages list
1294   ///////////////////////////////////////////////////////////////////
1295   return _packages._list;
1296 }
1297
1298 ///////////////////////////////////////////////////////////////////
1299 //
1300 //
1301 //      METHOD NAME : RpmDb::fileList
1302 //      METHOD TYPE : bool
1303 //
1304 //      DESCRIPTION :
1305 //
1306 list<FileInfo>
1307 RpmDb::fileList( const string & name_r, const Edition & edition_r ) const
1308 {
1309   list<FileInfo> result;
1310
1311   librpmDb::db_const_iterator it;
1312   bool found;
1313   if (edition_r == Edition::noedition)
1314   {
1315     found = it.findPackage( name_r );
1316   }
1317   else
1318   {
1319     found = it.findPackage( name_r, edition_r );
1320   }
1321   if (!found)
1322     return result;
1323
1324   return result;
1325 }
1326
1327
1328 ///////////////////////////////////////////////////////////////////
1329 //
1330 //
1331 //      METHOD NAME : RpmDb::hasFile
1332 //      METHOD TYPE : bool
1333 //
1334 //      DESCRIPTION :
1335 //
1336 bool RpmDb::hasFile( const string & file_r, const string & name_r ) const
1337 {
1338   librpmDb::db_const_iterator it;
1339   bool res;
1340   do
1341   {
1342     res = it.findByFile( file_r );
1343     if (!res) break;
1344     if (!name_r.empty())
1345     {
1346       res = (it->tag_name() == name_r);
1347     }
1348     ++it;
1349   }
1350   while (res && *it);
1351   return res;
1352 }
1353
1354 ///////////////////////////////////////////////////////////////////
1355 //
1356 //
1357 //      METHOD NAME : RpmDb::whoOwnsFile
1358 //      METHOD TYPE : string
1359 //
1360 //      DESCRIPTION :
1361 //
1362 string RpmDb::whoOwnsFile( const string & file_r) const
1363 {
1364   librpmDb::db_const_iterator it;
1365   if (it.findByFile( file_r ))
1366   {
1367     return it->tag_name();
1368   }
1369   return "";
1370 }
1371
1372 ///////////////////////////////////////////////////////////////////
1373 //
1374 //
1375 //      METHOD NAME : RpmDb::hasProvides
1376 //      METHOD TYPE : bool
1377 //
1378 //      DESCRIPTION :
1379 //
1380 bool RpmDb::hasProvides( const string & tag_r ) const
1381 {
1382   librpmDb::db_const_iterator it;
1383   return it.findByProvides( tag_r );
1384 }
1385
1386 ///////////////////////////////////////////////////////////////////
1387 //
1388 //
1389 //      METHOD NAME : RpmDb::hasRequiredBy
1390 //      METHOD TYPE : bool
1391 //
1392 //      DESCRIPTION :
1393 //
1394 bool RpmDb::hasRequiredBy( const string & tag_r ) const
1395 {
1396   librpmDb::db_const_iterator it;
1397   return it.findByRequiredBy( tag_r );
1398 }
1399
1400 ///////////////////////////////////////////////////////////////////
1401 //
1402 //
1403 //      METHOD NAME : RpmDb::hasConflicts
1404 //      METHOD TYPE : bool
1405 //
1406 //      DESCRIPTION :
1407 //
1408 bool RpmDb::hasConflicts( const string & tag_r ) const
1409 {
1410   librpmDb::db_const_iterator it;
1411   return it.findByConflicts( tag_r );
1412 }
1413
1414 ///////////////////////////////////////////////////////////////////
1415 //
1416 //
1417 //      METHOD NAME : RpmDb::hasPackage
1418 //      METHOD TYPE : bool
1419 //
1420 //      DESCRIPTION :
1421 //
1422 bool RpmDb::hasPackage( const string & name_r ) const
1423 {
1424   librpmDb::db_const_iterator it;
1425   return it.findPackage( name_r );
1426 }
1427
1428 ///////////////////////////////////////////////////////////////////
1429 //
1430 //
1431 //      METHOD NAME : RpmDb::hasPackage
1432 //      METHOD TYPE : bool
1433 //
1434 //      DESCRIPTION :
1435 //
1436 bool RpmDb::hasPackage( const string & name_r, const Edition & ed_r ) const
1437 {
1438   librpmDb::db_const_iterator it;
1439   return it.findPackage( name_r, ed_r );
1440 }
1441
1442 ///////////////////////////////////////////////////////////////////
1443 //
1444 //
1445 //      METHOD NAME : RpmDb::getData
1446 //      METHOD TYPE : PMError
1447 //
1448 //      DESCRIPTION :
1449 //
1450 void RpmDb::getData( const string & name_r,
1451                      RpmHeader::constPtr & result_r ) const
1452 {
1453   librpmDb::db_const_iterator it;
1454   it.findPackage( name_r );
1455   result_r = *it;
1456   if (it.dbError())
1457     ZYPP_THROW(*(it.dbError()));
1458 }
1459
1460 ///////////////////////////////////////////////////////////////////
1461 //
1462 //
1463 //      METHOD NAME : RpmDb::getData
1464 //      METHOD TYPE : void
1465 //
1466 //      DESCRIPTION :
1467 //
1468 void RpmDb::getData( const string & name_r, const Edition & ed_r,
1469                      RpmHeader::constPtr & result_r ) const
1470 {
1471   librpmDb::db_const_iterator it;
1472   it.findPackage( name_r, ed_r  );
1473   result_r = *it;
1474   if (it.dbError())
1475     ZYPP_THROW(*(it.dbError()));
1476 }
1477
1478 ///////////////////////////////////////////////////////////////////
1479 //
1480 //      METHOD NAME : RpmDb::checkPackage
1481 //      METHOD TYPE : RpmDb::checkPackageResult
1482 //
1483 RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r )
1484 {
1485   PathInfo file( path_r );
1486   if ( ! file.isFile() )
1487   {
1488     ERR << "Not a file: " << file << endl;
1489     return CHK_ERROR;
1490   }
1491
1492   FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
1493   if ( fd == 0 || ::Ferror(fd) )
1494   {
1495     ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
1496     if ( fd )
1497       ::Fclose( fd );
1498     return CHK_ERROR;
1499   }
1500
1501   rpmts ts = ::rpmtsCreate();
1502   ::rpmtsSetRootDir( ts, root().asString().c_str() );
1503   ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
1504   int res = ::rpmReadPackageFile( ts, fd, path_r.asString().c_str(), NULL );
1505   ts = ::rpmtsFree(ts);
1506
1507   ::Fclose( fd );
1508
1509   switch ( res )
1510   {
1511   case RPMRC_OK:
1512     return CHK_OK;
1513     break;
1514   case RPMRC_NOTFOUND:
1515     WAR << "Signature is unknown type. " << file << endl;
1516     return CHK_NOTFOUND;
1517     break;
1518   case RPMRC_FAIL:
1519     WAR << "Signature does not verify. " << file << endl;
1520     return CHK_FAIL;
1521     break;
1522   case RPMRC_NOTTRUSTED:
1523     WAR << "Signature is OK, but key is not trusted. " << file << endl;
1524     return CHK_NOTTRUSTED;
1525     break;
1526   case RPMRC_NOKEY:
1527     WAR << "Public key is unavailable. " << file << endl;
1528     return CHK_NOKEY;
1529     break;
1530   }
1531   ERR << "Error reading header." << file << endl;
1532   return CHK_ERROR;
1533 }
1534
1535 // determine changed files of installed package
1536 bool
1537 RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
1538 {
1539   bool ok = true;
1540
1541   fileList.clear();
1542
1543   if ( ! initialized() ) return false;
1544
1545   RpmArgVec opts;
1546
1547   opts.push_back ("-V");
1548   opts.push_back ("--nodeps");
1549   opts.push_back ("--noscripts");
1550   opts.push_back ("--nomd5");
1551   opts.push_back ("--");
1552   opts.push_back (packageName.c_str());
1553
1554   run_rpm (opts, ExternalProgram::Discard_Stderr);
1555
1556   if ( process == NULL )
1557     return false;
1558
1559   /* from rpm manpage
1560    5      MD5 sum
1561    S      File size
1562    L      Symlink
1563    T      Mtime
1564    D      Device
1565    U      User
1566    G      Group
1567    M      Mode (includes permissions and file type)
1568   */
1569
1570   string line;
1571   while (systemReadLine(line))
1572   {
1573     if (line.length() > 12 &&
1574         (line[0] == 'S' || line[0] == 's' ||
1575          (line[0] == '.' && line[7] == 'T')))
1576     {
1577       // file has been changed
1578       string filename;
1579
1580       filename.assign(line, 11, line.length() - 11);
1581       fileList.insert(filename);
1582     }
1583   }
1584
1585   systemStatus();
1586   // exit code ignored, rpm returns 1 no matter if package is installed or
1587   // not
1588
1589   return ok;
1590 }
1591
1592
1593
1594 /****************************************************************/
1595 /* private member-functions                                     */
1596 /****************************************************************/
1597
1598 /*--------------------------------------------------------------*/
1599 /* Run rpm with the specified arguments, handling stderr        */
1600 /* as specified  by disp                                        */
1601 /*--------------------------------------------------------------*/
1602 void
1603 RpmDb::run_rpm (const RpmArgVec& opts,
1604                 ExternalProgram::Stderr_Disposition disp)
1605 {
1606   if ( process )
1607   {
1608     delete process;
1609     process = NULL;
1610   }
1611   exit_code = -1;
1612
1613   if ( ! initialized() )
1614   {
1615     ZYPP_THROW(RpmDbNotOpenException());
1616   }
1617
1618   RpmArgVec args;
1619
1620   // always set root and dbpath
1621   args.push_back("rpm");
1622   args.push_back("--root");
1623   args.push_back(_root.asString().c_str());
1624   args.push_back("--dbpath");
1625   args.push_back(_dbPath.asString().c_str());
1626
1627   const char* argv[args.size() + opts.size() + 1];
1628
1629   const char** p = argv;
1630   p = copy (args.begin (), args.end (), p);
1631   p = copy (opts.begin (), opts.end (), p);
1632   *p = 0;
1633
1634   // Invalidate all outstanding database handles in case
1635   // the database gets modified.
1636   librpmDb::dbRelease( true );
1637
1638   // Launch the program with default locale
1639   process = new ExternalProgram(argv, disp, false, -1, true);
1640   return;
1641 }
1642
1643 /*--------------------------------------------------------------*/
1644 /* Read a line from the rpm process                             */
1645 /*--------------------------------------------------------------*/
1646 bool
1647 RpmDb::systemReadLine(string &line)
1648 {
1649   line.erase();
1650
1651   if ( process == NULL )
1652     return false;
1653
1654   line = process->receiveLine();
1655
1656   if (line.length() == 0)
1657     return false;
1658
1659   if (line[line.length() - 1] == '\n')
1660     line.erase(line.length() - 1);
1661
1662   return true;
1663 }
1664
1665 /*--------------------------------------------------------------*/
1666 /* Return the exit status of the rpm process, closing the       */
1667 /* connection if not already done                               */
1668 /*--------------------------------------------------------------*/
1669 int
1670 RpmDb::systemStatus()
1671 {
1672   if ( process == NULL )
1673     return -1;
1674
1675   exit_code = process->close();
1676   process->kill();
1677   delete process;
1678   process = 0;
1679
1680   //   DBG << "exit code " << exit_code << endl;
1681
1682   return exit_code;
1683 }
1684
1685 /*--------------------------------------------------------------*/
1686 /* Forcably kill the rpm process                                */
1687 /*--------------------------------------------------------------*/
1688 void
1689 RpmDb::systemKill()
1690 {
1691   if (process) process->kill();
1692 }
1693
1694
1695 // generate diff mails for config files
1696 void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
1697 {
1698   string msg = line.substr(9);
1699   string::size_type pos1 = string::npos;
1700   string::size_type pos2 = string::npos;
1701   string file1s, file2s;
1702   Pathname file1;
1703   Pathname file2;
1704
1705   pos1 = msg.find (typemsg);
1706   for (;;)
1707   {
1708     if ( pos1 == string::npos )
1709       break;
1710
1711     pos2 = pos1 + strlen (typemsg);
1712
1713     if (pos2 >= msg.length() )
1714       break;
1715
1716     file1 = msg.substr (0, pos1);
1717     file2 = msg.substr (pos2);
1718
1719     file1s = file1.asString();
1720     file2s = file2.asString();
1721
1722     if (!_root.empty() && _root != "/")
1723     {
1724       file1 = _root + file1;
1725       file2 = _root + file2;
1726     }
1727
1728     string out;
1729     int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
1730     if (ret)
1731     {
1732       Pathname file = _root + WARNINGMAILPATH;
1733       if (filesystem::assert_dir(file) != 0)
1734       {
1735         ERR << "Could not create " << file.asString() << endl;
1736         break;
1737       }
1738       file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
1739       ofstream notify(file.asString().c_str(), ios::out|ios::app);
1740       if (!notify)
1741       {
1742         ERR << "Could not open " <<  file << endl;
1743         break;
1744       }
1745
1746       // Translator: %s = name of an rpm package. A list of diffs follows
1747       // this message.
1748       notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
1749       if (ret>1)
1750       {
1751         ERR << "diff failed" << endl;
1752         notify << str::form(difffailmsg,
1753                             file1s.c_str(), file2s.c_str()) << endl;
1754       }
1755       else
1756       {
1757         notify << str::form(diffgenmsg,
1758                             file1s.c_str(), file2s.c_str()) << endl;
1759
1760         // remove root for the viewer's pleasure (#38240)
1761         if (!_root.empty() && _root != "/")
1762         {
1763           if (out.substr(0,4) == "--- ")
1764           {
1765             out.replace(4, file1.asString().length(), file1s);
1766           }
1767           string::size_type pos = out.find("\n+++ ");
1768           if (pos != string::npos)
1769           {
1770             out.replace(pos+5, file2.asString().length(), file2s);
1771           }
1772         }
1773         notify << out << endl;
1774       }
1775       notify.close();
1776       notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
1777       notify.close();
1778     }
1779     else
1780     {
1781       WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
1782     }
1783     break;
1784   }
1785 }
1786
1787 ///////////////////////////////////////////////////////////////////
1788 //
1789 //
1790 //      METHOD NAME : RpmDb::installPackage
1791 //      METHOD TYPE : PMError
1792 //
1793 void RpmDb::installPackage( const Pathname & filename, unsigned flags )
1794 {
1795   callback::SendReport<RpmInstallReport> report;
1796
1797   report->start(filename);
1798
1799   do
1800     try
1801     {
1802       doInstallPackage(filename, flags, report);
1803       report->finish();
1804       break;
1805     }
1806     catch (RpmException & excpt_r)
1807     {
1808       RpmInstallReport::Action user = report->problem( excpt_r );
1809
1810       if ( user == RpmInstallReport::ABORT )
1811       {
1812         report->finish( excpt_r );
1813         ZYPP_RETHROW(excpt_r);
1814       }
1815       else if ( user == RpmInstallReport::IGNORE )
1816       {
1817         break;
1818       }
1819     }
1820   while (true);
1821 }
1822
1823 void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, callback::SendReport<RpmInstallReport> & report )
1824 {
1825   FAILIFNOTINITIALIZED;
1826   CommitLog progresslog;
1827
1828   MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
1829
1830
1831   // backup
1832   if ( _packagebackups )
1833   {
1834     // FIXME      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1835     if ( ! backupPackage( filename ) )
1836     {
1837       ERR << "backup of " << filename.asString() << " failed" << endl;
1838     }
1839     // FIXME status handling
1840     report->progress( 0 ); // allow 1% for backup creation.
1841   }
1842   else
1843   {
1844     report->progress( 100 );
1845   }
1846
1847   // run rpm
1848   RpmArgVec opts;
1849   if (flags & RPMINST_NOUPGRADE)
1850     opts.push_back("-i");
1851   else
1852     opts.push_back("-U");
1853   opts.push_back("--percent");
1854
1855   if (flags & RPMINST_NODIGEST)
1856     opts.push_back("--nodigest");
1857   if (flags & RPMINST_NOSIGNATURE)
1858     opts.push_back("--nosignature");
1859   if (flags & RPMINST_NODOCS)
1860     opts.push_back ("--excludedocs");
1861   if (flags & RPMINST_NOSCRIPTS)
1862     opts.push_back ("--noscripts");
1863   if (flags & RPMINST_FORCE)
1864     opts.push_back ("--force");
1865   if (flags & RPMINST_NODEPS)
1866     opts.push_back ("--nodeps");
1867   if (flags & RPMINST_IGNORESIZE)
1868     opts.push_back ("--ignoresize");
1869   if (flags & RPMINST_JUSTDB)
1870     opts.push_back ("--justdb");
1871   if (flags & RPMINST_TEST)
1872     opts.push_back ("--test");
1873
1874   opts.push_back("--");
1875
1876   // rpm requires additional quoting of special chars:
1877   string quotedFilename( rpmQuoteFilename( filename ) );
1878   opts.push_back ( quotedFilename.c_str() );
1879
1880   modifyDatabase(); // BEFORE run_rpm
1881   run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
1882
1883   string line;
1884   string rpmmsg;
1885   vector<string> configwarnings;
1886   vector<string> errorlines;
1887
1888   while (systemReadLine(line))
1889   {
1890     if (line.substr(0,2)=="%%")
1891     {
1892       int percent;
1893       sscanf (line.c_str () + 2, "%d", &percent);
1894       report->progress( percent );
1895     }
1896     else
1897       rpmmsg += line+'\n';
1898
1899     if ( line.substr(0,8) == "warning:" )
1900     {
1901       configwarnings.push_back(line);
1902     }
1903   }
1904   int rpm_status = systemStatus();
1905
1906   // evaluate result
1907   for (vector<string>::iterator it = configwarnings.begin();
1908        it != configwarnings.end(); ++it)
1909   {
1910     processConfigFiles(*it, Pathname::basename(filename), " saved as ",
1911                        // %s = filenames
1912                        _("rpm saved %s as %s but it was impossible to determine the difference"),
1913                        // %s = filenames
1914                        _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
1915     processConfigFiles(*it, Pathname::basename(filename), " created as ",
1916                        // %s = filenames
1917                        _("rpm created %s as %s but it was impossible to determine the difference"),
1918                        // %s = filenames
1919                        _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
1920   }
1921
1922   if ( rpm_status != 0 )
1923   {
1924     // %s = filename of rpm package
1925     progresslog(/*timestamp*/true) << str::form(_("%s install failed"), Pathname::basename(filename).c_str()) << endl;
1926     progresslog() << _("rpm output:") << endl << rpmmsg << endl;
1927     ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
1928   }
1929   else
1930   {
1931     // %s = filename of rpm package
1932     progresslog(/*timestamp*/true) << str::form(_("%s installed ok"), Pathname::basename(filename).c_str()) << endl;
1933     if ( ! rpmmsg.empty() )
1934     {
1935       progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
1936     }
1937   }
1938 }
1939
1940 ///////////////////////////////////////////////////////////////////
1941 //
1942 //
1943 //      METHOD NAME : RpmDb::removePackage
1944 //      METHOD TYPE : PMError
1945 //
1946 void RpmDb::removePackage( Package::constPtr package, unsigned flags )
1947 {
1948   return removePackage( package->name()
1949                         + "-" + package->edition().asString()
1950                         + "." + package->arch().asString(), flags );
1951 }
1952
1953 ///////////////////////////////////////////////////////////////////
1954 //
1955 //
1956 //      METHOD NAME : RpmDb::removePackage
1957 //      METHOD TYPE : PMError
1958 //
1959 void RpmDb::removePackage( const string & name_r, unsigned flags )
1960 {
1961   callback::SendReport<RpmRemoveReport> report;
1962
1963   report->start( name_r );
1964
1965   try
1966   {
1967     doRemovePackage(name_r, flags, report);
1968   }
1969   catch (RpmException & excpt_r)
1970   {
1971     report->finish(excpt_r);
1972     ZYPP_RETHROW(excpt_r);
1973   }
1974   report->finish();
1975 }
1976
1977
1978 void RpmDb::doRemovePackage( const string & name_r, unsigned flags, callback::SendReport<RpmRemoveReport> & report )
1979 {
1980   FAILIFNOTINITIALIZED;
1981   CommitLog progresslog;
1982
1983   MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
1984
1985   // backup
1986   if ( _packagebackups )
1987   {
1988     // FIXME solve this status report somehow
1989     //      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
1990     if ( ! backupPackage( name_r ) )
1991     {
1992       ERR << "backup of " << name_r << " failed" << endl;
1993     }
1994     report->progress( 0 );
1995   }
1996   else
1997   {
1998     report->progress( 100 );
1999   }
2000
2001   // run rpm
2002   RpmArgVec opts;
2003   opts.push_back("-e");
2004   opts.push_back("--allmatches");
2005
2006   if (flags & RPMINST_NOSCRIPTS)
2007     opts.push_back("--noscripts");
2008   if (flags & RPMINST_NODEPS)
2009     opts.push_back("--nodeps");
2010   if (flags & RPMINST_JUSTDB)
2011     opts.push_back("--justdb");
2012   if (flags & RPMINST_TEST)
2013     opts.push_back ("--test");
2014   if (flags & RPMINST_FORCE)
2015   {
2016     WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
2017   }
2018
2019   opts.push_back("--");
2020   opts.push_back(name_r.c_str());
2021
2022   modifyDatabase(); // BEFORE run_rpm
2023   run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
2024
2025   string line;
2026   string rpmmsg;
2027
2028   // got no progress from command, so we fake it:
2029   // 5  - command started
2030   // 50 - command completed
2031   // 100 if no error
2032   report->progress( 5 );
2033   while (systemReadLine(line))
2034   {
2035     rpmmsg += line+'\n';
2036   }
2037   report->progress( 50 );
2038   int rpm_status = systemStatus();
2039
2040   if ( rpm_status != 0 )
2041   {
2042     // %s = name of rpm package
2043     progresslog(/*timestamp*/true) << str::form(_("%s remove failed"), name_r.c_str()) << endl;
2044     progresslog() << _("rpm output:") << endl << rpmmsg << endl;
2045     ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
2046   }
2047   else
2048   {
2049     progresslog(/*timestamp*/true) << str::form(_("%s remove ok"), name_r.c_str()) << endl;
2050     if ( ! rpmmsg.empty() )
2051     {
2052       progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
2053     }
2054   }
2055 }
2056
2057 ///////////////////////////////////////////////////////////////////
2058 //
2059 //
2060 //      METHOD NAME : RpmDb::backupPackage
2061 //      METHOD TYPE : bool
2062 //
2063 bool RpmDb::backupPackage( const Pathname & filename )
2064 {
2065   RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
2066   if ( ! h )
2067     return false;
2068
2069   return backupPackage( h->tag_name() );
2070 }
2071
2072 ///////////////////////////////////////////////////////////////////
2073 //
2074 //
2075 //      METHOD NAME : RpmDb::backupPackage
2076 //      METHOD TYPE : bool
2077 //
2078 bool RpmDb::backupPackage(const string& packageName)
2079 {
2080   CommitLog progresslog;
2081   bool ret = true;
2082   Pathname backupFilename;
2083   Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
2084
2085   if (_backuppath.empty())
2086   {
2087     INT << "_backuppath empty" << endl;
2088     return false;
2089   }
2090
2091   FileList fileList;
2092
2093   if (!queryChangedFiles(fileList, packageName))
2094   {
2095     ERR << "Error while getting changed files for package " <<
2096     packageName << endl;
2097     return false;
2098   }
2099
2100   if (fileList.size() <= 0)
2101   {
2102     DBG <<  "package " <<  packageName << " not changed -> no backup" << endl;
2103     return true;
2104   }
2105
2106   if (filesystem::assert_dir(_root + _backuppath) != 0)
2107   {
2108     return false;
2109   }
2110
2111   {
2112     // build up archive name
2113     time_t currentTime = time(0);
2114     struct tm *currentLocalTime = localtime(&currentTime);
2115
2116     int date = (currentLocalTime->tm_year + 1900) * 10000
2117                + (currentLocalTime->tm_mon + 1) * 100
2118                + currentLocalTime->tm_mday;
2119
2120     int num = 0;
2121     do
2122     {
2123       backupFilename = _root + _backuppath
2124                        + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
2125
2126     }
2127     while ( PathInfo(backupFilename).isExist() && num++ < 1000);
2128
2129     PathInfo pi(filestobackupfile);
2130     if (pi.isExist() && !pi.isFile())
2131     {
2132       ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
2133       return false;
2134     }
2135
2136     ofstream fp ( filestobackupfile.asString().c_str(), ios::out|ios::trunc );
2137
2138     if (!fp)
2139     {
2140       ERR << "could not open " << filestobackupfile.asString() << endl;
2141       return false;
2142     }
2143
2144     for (FileList::const_iterator cit = fileList.begin();
2145          cit != fileList.end(); ++cit)
2146     {
2147       string name = *cit;
2148       if ( name[0] == '/' )
2149       {
2150         // remove slash, file must be relative to -C parameter of tar
2151         name = name.substr( 1 );
2152       }
2153       DBG << "saving file "<< name << endl;
2154       fp << name << endl;
2155     }
2156     fp.close();
2157
2158     const char* const argv[] =
2159       {
2160         "tar",
2161         "-czhP",
2162         "-C",
2163         _root.asString().c_str(),
2164         "--ignore-failed-read",
2165         "-f",
2166         backupFilename.asString().c_str(),
2167         "-T",
2168         filestobackupfile.asString().c_str(),
2169         NULL
2170       };
2171
2172     // execute tar in inst-sys (we dont know if there is a tar below _root !)
2173     ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
2174
2175     string tarmsg;
2176
2177     // TODO: its probably possible to start tar with -v and watch it adding
2178     // files to report progress
2179     for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
2180     {
2181       tarmsg+=output;
2182     }
2183
2184     int ret = tar.close();
2185
2186     if ( ret != 0)
2187     {
2188       ERR << "tar failed: " << tarmsg << endl;
2189       ret = false;
2190     }
2191     else
2192     {
2193       MIL << "tar backup ok" << endl;
2194       progresslog(/*timestamp*/true) << str::form(_("created backup %s"), backupFilename.asString().c_str()) << endl;
2195     }
2196
2197     filesystem::unlink(filestobackupfile);
2198   }
2199
2200   return ret;
2201 }
2202
2203 void RpmDb::setBackupPath(const Pathname& path)
2204 {
2205   _backuppath = path;
2206 }
2207
2208 } // namespace rpm
2209 } // namespace target
2210 } // namespace zypp