Imported Upstream version 17.23.5
[platform/upstream/libzypp.git] / zypp / HistoryLog.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/HistoryLog.cc
10  *
11  */
12 #include <iostream>
13 #include <fstream>
14 #include <unistd.h>
15
16 #include <zypp/ZConfig.h>
17 #include <zypp/base/String.h>
18 #include <zypp/base/Logger.h>
19 #include <zypp/base/IOStream.h>
20
21 #include <zypp/PathInfo.h>
22 #include <zypp/Date.h>
23
24 #include <zypp/PoolItem.h>
25 #include <zypp/Package.h>
26 #include <zypp/RepoInfo.h>
27
28 #include <zypp/HistoryLog.h>
29 #include <zypp/HistoryLogData.h>
30
31 using std::endl;
32 using std::string;
33
34 namespace zypp
35 {
36   namespace
37   {
38     inline string timestamp()
39     { return zypp::Date::now().form( HISTORY_LOG_DATE_FORMAT ); }
40
41     inline string userAtHostname()
42     {
43       static char buf[256];
44       string result;
45       char * tmp = ::cuserid(buf);
46       if (tmp)
47       {
48         result = string(tmp);
49         if (!::gethostname(buf, 255))
50           result += "@" + string(buf);
51       }
52       return result;
53     }
54
55     static std::string pidAndAppname()
56     {
57       static std::string _val;
58       if ( _val.empty() )
59       {
60         pid_t mypid = getpid();
61         zypp::Pathname p( "/proc/"+zypp::str::numstring(mypid)+"/exe" );
62         zypp::Pathname myname( zypp::filesystem::readlink( p ) );
63
64         _val += zypp::str::numstring(mypid);
65         _val += ":";
66         _val += myname.basename();
67       }
68       return _val;
69     }
70
71     static std::string cmdline()
72     {
73       static std::string _val;
74       if ( _val.empty() )
75       {
76         pid_t mypid = getpid();
77         {
78           std::ifstream cmdlineStr( Pathname("/proc/"+zypp::str::numstring(mypid)+"/cmdline").c_str() );
79           char ch;
80           const char * sep = "'";
81           while ( cmdlineStr && cmdlineStr.get( ch ) )
82           {
83             if ( sep )
84             {
85               _val += sep;
86               sep = nullptr;
87             }
88             switch ( ch )
89             {
90               case '\0':        _val += '\''; sep = " '"; break;
91               case '\n':        _val += ' '; break;
92               case '\\':        _val += '\\'; _val += '\\'; break;
93               case '|':         _val += '\\'; _val += '|'; break;
94               default:          _val += ch; break;
95             }
96           }
97         }
98       }
99       return _val;
100     }
101   } // namespace
102
103   namespace
104   {
105     const char          _sep = '|';
106     std::ofstream       _log;
107     unsigned            _refcnt = 0;
108     Pathname            _fname;
109     Pathname            _fnameLastFail;
110
111     inline void openLog()
112     {
113       if ( _fname.empty() )
114         _fname = ZConfig::instance().historyLogFile();
115
116       _log.clear();
117       _log.open( _fname.asString().c_str(), std::ios::out|std::ios::app );
118       if( !_log && _fnameLastFail != _fname )
119       {
120         ERR << "Could not open logfile '" << _fname << "'" << endl;
121         _fnameLastFail = _fname;
122       }
123     }
124
125     inline void closeLog()
126     {
127       _log.clear();
128       _log.close();
129     }
130
131     inline void refUp()
132     {
133       if ( !_refcnt )
134         openLog();
135       ++_refcnt;
136     }
137
138     inline void refDown()
139     {
140       --_refcnt;
141       if ( !_refcnt )
142         closeLog();
143     }
144   } // namespace
145
146   ///////////////////////////////////////////////////////////////////
147   //
148   //    CLASS NAME : HistoryLog
149   //
150   ///////////////////////////////////////////////////////////////////
151
152   HistoryLog::HistoryLog( const Pathname & rootdir )
153   {
154     setRoot( rootdir );
155     refUp();
156   }
157
158   HistoryLog::~HistoryLog()
159   {
160     refDown();
161   }
162
163   void HistoryLog::setRoot( const Pathname & rootdir )
164   {
165     if ( ! rootdir.absolute() )
166       return;
167
168     if ( _refcnt )
169       closeLog();
170
171     _fname = rootdir / ZConfig::instance().historyLogFile();
172     filesystem::assert_dir( _fname.dirname() );
173     MIL << "installation log file " << _fname << endl;
174
175     if ( _refcnt )
176       openLog();
177   }
178
179   const Pathname & HistoryLog::fname()
180   {
181     if ( _fname.empty() )
182       _fname = ZConfig::instance().historyLogFile();
183     return _fname;
184   }
185
186   /////////////////////////////////////////////////////////////////////////
187
188   void HistoryLog::comment( const string & comment, bool timestamp_r )
189   {
190     if (comment.empty())
191       return;
192
193     _log << "# ";
194     if ( timestamp_r )
195       _log << timestamp() << " ";
196
197     const char * s = comment.c_str();
198     const char * c = s;
199     unsigned size = comment.size();
200
201     // ignore the last newline
202     if (comment[size-1] == '\n')
203       --size;
204
205     for ( unsigned i = 0; i < size; ++i, ++c )
206       if ( *c == '\n' )
207       {
208         _log << string( s, c + 1 - s ) << "# ";
209         s = c + 1;
210       }
211
212     if ( s < c )
213       _log << std::string( s, c-s );
214
215     _log << endl;
216   }
217
218   /////////////////////////////////////////////////////////////////////////
219
220   void HistoryLog::stampCommand()
221   {
222     _log
223       << timestamp()                                                    // 1 timestamp
224       << _sep << HistoryActionID::STAMP_COMMAND.asString(true)          // 2 action
225       << _sep << userAtHostname()                                       // 3 requested by
226       << _sep << cmdline()                                              // 4 command
227       << _sep << str::escape(ZConfig::instance().userData(), _sep)      // 6 userdata
228       << endl;
229
230   }
231
232   void HistoryLog::install( const PoolItem & pi )
233   {
234     if ( ! pi.isKind<Package>() ) return;
235     const Package::constPtr p = asKind<Package>(pi.resolvable());
236
237     _log
238       << timestamp()                                                    // 1 timestamp
239       << _sep << HistoryActionID::INSTALL.asString(true)                // 2 action
240       << _sep << p->name()                                              // 3 name
241       << _sep << p->edition()                                           // 4 evr
242       << _sep << p->arch();                                             // 5 arch
243
244     // ApplLow is what the solver selected on behalf of the user.
245     if (pi.status().isByUser() || pi.status().isByApplLow() )
246       _log << _sep << userAtHostname();                                 // 6 requested by
247     else if (pi.status().isByApplHigh())
248       _log << _sep << pidAndAppname();
249     else
250       _log << _sep;
251
252     _log
253       << _sep << p->repoInfo().alias()                                  // 7 repo alias
254       << _sep << p->checksum().checksum()                               // 8 checksum
255       << _sep << str::escape(ZConfig::instance().userData(), _sep)      // 9 userdata
256       << endl;
257   }
258
259
260   void HistoryLog::remove( const PoolItem & pi )
261   {
262     if ( ! pi.isKind<Package>() ) return;
263     const Package::constPtr p = asKind<Package>(pi.resolvable());
264
265     _log
266       << timestamp()                                                    // 1 timestamp
267       << _sep << HistoryActionID::REMOVE.asString(true)                 // 2 action
268       << _sep << p->name()                                              // 3 name
269       << _sep << p->edition()                                           // 4 evr
270       << _sep << p->arch();                                             // 5 arch
271
272     // ApplLow is what the solver selected on behalf of the user.
273     if ( pi.status().isByUser() || pi.status().isByApplLow() )
274       _log << _sep << userAtHostname();                                 // 6 requested by
275     else if (pi.status().isByApplHigh())
276       _log << _sep << pidAndAppname();
277     else
278       _log << _sep;
279
280     _log
281       << _sep << str::escape(ZConfig::instance().userData(), _sep)      // 7 userdata
282       << endl;
283   }
284
285   /////////////////////////////////////////////////////////////////////////
286
287   void HistoryLog::addRepository(const RepoInfo & repo)
288   {
289     _log
290       << timestamp()                                                    // 1 timestamp
291       << _sep << HistoryActionID::REPO_ADD.asString(true)               // 2 action
292       << _sep << str::escape(repo.alias(), _sep)                        // 3 alias
293       << _sep << str::escape(repo.url().asString(), _sep)               // 4 primary URL
294       << _sep << str::escape(ZConfig::instance().userData(), _sep)      // 5 userdata
295       << endl;
296   }
297
298
299   void HistoryLog::removeRepository(const RepoInfo & repo)
300   {
301     _log
302       << timestamp()                                                    // 1 timestamp
303       << _sep << HistoryActionID::REPO_REMOVE.asString(true)            // 2 action
304       << _sep << str::escape(repo.alias(), _sep)                        // 3 alias
305       << _sep << str::escape(ZConfig::instance().userData(), _sep)      // 4 userdata
306       << endl;
307   }
308
309
310   void HistoryLog::modifyRepository(
311       const RepoInfo & oldrepo, const RepoInfo & newrepo)
312   {
313     if (oldrepo.alias() != newrepo.alias())
314     {
315       _log
316         << timestamp()                                                  // 1 timestamp
317         << _sep << HistoryActionID::REPO_CHANGE_ALIAS.asString(true)    // 2 action
318         << _sep << str::escape(oldrepo.alias(), _sep)                   // 3 old alias
319         << _sep << str::escape(newrepo.alias(), _sep)                   // 4 new alias
320         << _sep << str::escape(ZConfig::instance().userData(), _sep)    // 5 userdata
321         << endl;
322     }
323     if ( oldrepo.url() != newrepo.url() )
324     {
325       _log
326         << timestamp()                                                  // 1 timestamp
327         << _sep << HistoryActionID::REPO_CHANGE_URL.asString(true)      // 2 action
328         << _sep << str::escape(oldrepo.url().asString(), _sep)          // 3 old url
329         << _sep << str::escape(newrepo.url().asString(), _sep)          // 4 new url
330         << _sep << str::escape(ZConfig::instance().userData(), _sep)    // 5 userdata
331         << endl;
332     }
333   }
334
335   void HistoryLog::patchStateChange( const PoolItem & pi, ResStatus::ValidateValue oldstate )
336   {
337     if ( ! pi.isKind<Patch>() ) return;
338     const Patch::constPtr p = asKind<Patch>(pi.resolvable());
339
340     _log
341       << timestamp()                                                    // 1 timestamp
342       << _sep << HistoryActionID::PATCH_STATE_CHANGE.asString(true)     // 2 action
343       << _sep << p->name()                                              // 3 name
344       << _sep << p->edition()                                           // 4 evr
345       << _sep << p->arch()                                              // 5 arch
346       << _sep << p->repoInfo().alias()                                  // 6 repo alias
347       << _sep << p->severity()                                          // 7 severity
348       << _sep << p->category()                                          // 8 category
349       << _sep << ResStatus::validateValueAsString( oldstate )           // 9 old state
350       << _sep << pi.status().validateValueAsString()                    // 10 new state
351       << _sep << str::escape(ZConfig::instance().userData(), _sep)      // 11 userdata
352       << endl;
353   }
354
355   ///////////////////////////////////////////////////////////////////
356
357 } // namespace zypp