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