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