Imported Upstream version 17.23.5
[platform/upstream/libzypp.git] / zypp / target / RpmPostTransCollector.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/target/RpmPostTransCollector.cc
10  */
11 #include <iostream>
12 #include <fstream>
13 #include <zypp/base/LogTools.h>
14 #include <zypp/base/NonCopyable.h>
15 #include <zypp/base/Gettext.h>
16 #include <zypp/target/RpmPostTransCollector.h>
17
18 #include <zypp/TmpPath.h>
19 #include <zypp/PathInfo.h>
20 #include <zypp/HistoryLog.h>
21 #include <zypp/ZYppCallbacks.h>
22 #include <zypp/ExternalProgram.h>
23 #include <zypp/target/rpm/RpmHeader.h>
24 #include <zypp/target/rpm/librpmDb.h>
25 #include <zypp/ZConfig.h>
26 #include <zypp/ZYppCallbacks.h>
27
28 using std::endl;
29 #undef ZYPP_BASE_LOGGER_LOGGROUP
30 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::posttrans"
31
32 ///////////////////////////////////////////////////////////////////
33 namespace zypp
34 {
35   ///////////////////////////////////////////////////////////////////
36   namespace target
37   {
38
39     ///////////////////////////////////////////////////////////////////
40     /// \class RpmPostTransCollector::Impl
41     /// \brief RpmPostTransCollector implementation.
42     ///////////////////////////////////////////////////////////////////
43     class RpmPostTransCollector::Impl : private base::NonCopyable
44     {
45       friend std::ostream & operator<<( std::ostream & str, const Impl & obj );
46       friend std::ostream & dumpOn( std::ostream & str, const Impl & obj );
47       public:
48         Impl( const Pathname & root_r )
49         : _root( root_r )
50         {}
51
52         ~Impl()
53         { if ( !_scripts.empty() ) discardScripts(); }
54
55         /** Extract and remember a packages %posttrans script for later execution. */
56         bool collectScriptFromPackage( ManagedFile rpmPackage_r )
57         {
58           rpm::RpmHeader::constPtr pkg( rpm::RpmHeader::readPackage( rpmPackage_r, rpm::RpmHeader::NOVERIFY ) );
59           if ( ! pkg )
60           {
61             WAR << "Unexpectedly this is no package: " << rpmPackage_r << endl;
62             return false;
63           }
64
65           std::string prog( pkg->tag_posttransprog() );
66           if ( prog.empty() || prog == "<lua>" )        // by now leave lua to rpm
67             return false;
68
69           filesystem::TmpFile script( tmpDir(), rpmPackage_r->basename() );
70           filesystem::addmod( script.path(), 0500 );
71           script.autoCleanup( false );  // no autodelete; within a tmpdir
72           {
73             std::ofstream out( script.path().c_str() );
74             out << "#! " << pkg->tag_posttransprog() << endl
75                 << pkg->tag_posttrans() << endl;
76           }
77           _scripts.push_back( std::make_pair( script.path().basename(), pkg->tag_name() ) );
78           MIL << "COLLECT posttrans: '" << PathInfo( script.path() ) << "' for package: '" << pkg->tag_name() << "'" << endl;
79           //DBG << "PROG:  " << pkg->tag_posttransprog() << endl;
80           //DBG << "SCRPT: " << pkg->tag_posttrans() << endl;
81           return true;
82         }
83
84         /** Execute the remembered scripts. */
85         bool executeScripts()
86         {
87           if ( _scripts.empty() )
88             return true;
89
90           HistoryLog historylog;
91
92           Pathname noRootScriptDir( ZConfig::instance().update_scriptsPath() / tmpDir().basename() );
93
94           ProgressData scriptProgress( static_cast<ProgressData::value_type>(_scripts.size()) );
95           callback::SendReport<ProgressReport> report;
96           scriptProgress.sendTo( ProgressReportAdaptor( ProgressData::ReceiverFnc(), report ) );
97
98           bool firstScript = true;
99           while ( ! _scripts.empty() )
100           {
101             const auto &scriptPair = _scripts.front();
102             const std::string & script = scriptPair.first;
103             const std::string & pkgident( script.substr( 0, script.size()-6 ) );        // strip tmp file suffix
104
105             scriptProgress.name( str::Format(_("Executing %%posttrans script '%1%'")) % pkgident );
106
107             bool canContinue = true;
108             if (firstScript)  {
109               firstScript = false;
110               canContinue = scriptProgress.toMin();
111             } else {
112               canContinue = scriptProgress.incr();
113             }
114
115             if (!canContinue) {
116               str::Str msg;
117               msg << "Execution of %posttrans scripts cancelled";
118               WAR << msg << endl;
119               historylog.comment( msg, true /*timestamp*/);
120               JobReport::warning( msg );
121               return false;
122             }
123
124             int npkgs = 0;
125             rpm::librpmDb::db_const_iterator it;
126             for ( it.findByName( scriptPair.second ); *it; ++it )
127               npkgs++;
128
129             MIL << "EXECUTE posttrans: " << script << " with argument: " << npkgs << endl;
130             ExternalProgram prog( (noRootScriptDir/script).asString() + " " +str::numstring( npkgs ), ExternalProgram::Stderr_To_Stdout, false, -1, true, _root );
131
132             str::Str collect;
133             for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
134             {
135               DBG << line;
136               collect << "    " << line;
137             }
138
139             //script was executed, remove it from the list
140             _scripts.pop_front();
141
142             int ret = prog.close();
143             const std::string & scriptmsg( collect );
144
145             if ( ret != 0 || ! scriptmsg.empty() )
146             {
147               if ( ! scriptmsg.empty() )
148               {
149                 str::Str msg;
150                 msg << "Output of " << pkgident << " %posttrans script:\n" << scriptmsg;
151                 historylog.comment( msg, true /*timestamp*/);
152                 JobReport::UserData userData( "cmdout", "%posttrans" );
153                 JobReport::info( msg, userData );
154               }
155
156               if ( ret != 0 )
157               {
158                 str::Str msg;
159                 msg << pkgident << " %posttrans script failed (returned " << ret << ")";
160                 WAR << msg << endl;
161                 historylog.comment( msg, true /*timestamp*/);
162                 JobReport::warning( msg );
163               }
164             }
165           }
166
167           //show a final message
168           scriptProgress.name( _("Executing %posttrans scripts") );
169           scriptProgress.toMax();
170           _scripts.clear();
171           return true;
172         }
173
174         /** Discard all remembered scrips. */
175         void discardScripts()
176         {
177           if ( _scripts.empty() )
178             return;
179
180           HistoryLog historylog;
181
182           str::Str msg;
183           msg << "%posttrans scripts skipped while aborting:\n";
184           for ( const auto & script : _scripts )
185           {
186             const std::string & pkgident( script.first.substr( 0, script.first.size()-6 ) );    // strip tmp file suffix
187             WAR << "UNEXECUTED posttrans: " << script.first << endl;
188             msg << "    " << pkgident << "\n";
189           }
190
191           historylog.comment( msg, true /*timestamp*/);
192           JobReport::warning( msg );
193
194           _scripts.clear();
195         }
196
197
198       private:
199         /** Lazy create tmpdir on demand. */
200         Pathname tmpDir()
201         {
202           if ( !_ptrTmpdir ) _ptrTmpdir.reset( new filesystem::TmpDir( _root / ZConfig::instance().update_scriptsPath(), "posttrans" ) );
203           DBG << _ptrTmpdir->path() << endl;
204           return _ptrTmpdir->path();
205         }
206
207       private:
208         Pathname _root;
209         std::list< std::pair< std::string, std::string > > _scripts;
210         boost::scoped_ptr<filesystem::TmpDir> _ptrTmpdir;
211     };
212
213     /** \relates RpmPostTransCollector::Impl Stream output */
214     inline std::ostream & operator<<( std::ostream & str, const RpmPostTransCollector::Impl & obj )
215     { return str << "RpmPostTransCollector::Impl"; }
216
217     /** \relates RpmPostTransCollector::Impl Verbose stream output */
218     inline std::ostream & dumpOn( std::ostream & str, const RpmPostTransCollector::Impl & obj )
219     { return str << obj; }
220
221     ///////////////////////////////////////////////////////////////////
222     //
223     //  CLASS NAME : RpmPostTransCollector
224     //
225     ///////////////////////////////////////////////////////////////////
226
227     RpmPostTransCollector::RpmPostTransCollector( const Pathname & root_r )
228       : _pimpl( new Impl( root_r ) )
229     {}
230
231     RpmPostTransCollector::~RpmPostTransCollector()
232     {}
233
234     bool RpmPostTransCollector::collectScriptFromPackage( ManagedFile rpmPackage_r )
235     { return _pimpl->collectScriptFromPackage( rpmPackage_r ); }
236
237     bool RpmPostTransCollector::executeScripts()
238     { return _pimpl->executeScripts(); }
239
240     void RpmPostTransCollector::discardScripts()
241     { return _pimpl->discardScripts(); }
242
243     std::ostream & operator<<( std::ostream & str, const RpmPostTransCollector & obj )
244     { return str << *obj._pimpl; }
245
246     std::ostream & dumpOn( std::ostream & str, const RpmPostTransCollector & obj )
247     { return dumpOn( str, *obj._pimpl ); }
248
249   } // namespace target
250   ///////////////////////////////////////////////////////////////////
251 } // namespace zypp
252 ///////////////////////////////////////////////////////////////////