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