Imported Upstream version 16.3.2
[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     const Package::constPtr p = asKind<Package>(pi.resolvable());
235     if (!p)
236       return;
237
238     _log
239       << timestamp()                                                    // 1 timestamp
240       << _sep << HistoryActionID::INSTALL.asString(true)                // 2 action
241       << _sep << p->name()                                              // 3 name
242       << _sep << p->edition()                                           // 4 evr
243       << _sep << p->arch();                                             // 5 arch
244
245     // ApplLow is what the solver selected on behalf of the user.
246     if (pi.status().isByUser() || pi.status().isByApplLow() )
247       _log << _sep << userAtHostname();                                 // 6 requested by
248     else if (pi.status().isByApplHigh())
249       _log << _sep << pidAndAppname();
250     else
251       _log << _sep;
252
253     _log
254       << _sep << p->repoInfo().alias()                                  // 7 repo alias
255       << _sep << p->checksum().checksum()                               // 8 checksum
256       << _sep << str::escape(ZConfig::instance().userData(), _sep)      // 9 userdata
257       << endl;
258   }
259
260
261   void HistoryLog::remove( const PoolItem & pi )
262   {
263     const Package::constPtr p = asKind<Package>(pi.resolvable());
264     if (!p)
265       return;
266
267     _log
268       << timestamp()                                                    // 1 timestamp
269       << _sep << HistoryActionID::REMOVE.asString(true)                 // 2 action
270       << _sep << p->name()                                              // 3 name
271       << _sep << p->edition()                                           // 4 evr
272       << _sep << p->arch();                                             // 5 arch
273
274     // ApplLow is what the solver selected on behalf of the user.
275     if ( pi.status().isByUser() || pi.status().isByApplLow() )
276       _log << _sep << userAtHostname();                                 // 6 requested by
277     else if (pi.status().isByApplHigh())
278       _log << _sep << pidAndAppname();
279     else
280       _log << _sep;
281
282     _log
283       << _sep << str::escape(ZConfig::instance().userData(), _sep)      // 7 userdata
284       << endl;
285   }
286
287   /////////////////////////////////////////////////////////////////////////
288
289   void HistoryLog::addRepository(const RepoInfo & repo)
290   {
291     _log
292       << timestamp()                                                    // 1 timestamp
293       << _sep << HistoryActionID::REPO_ADD.asString(true)               // 2 action
294       << _sep << str::escape(repo.alias(), _sep)                        // 3 alias
295       << _sep << *repo.baseUrlsBegin()                                  // 4 primary URL
296       << _sep << str::escape(ZConfig::instance().userData(), _sep)      // 5 userdata
297       << endl;
298   }
299
300
301   void HistoryLog::removeRepository(const RepoInfo & repo)
302   {
303     _log
304       << timestamp()                                                    // 1 timestamp
305       << _sep << HistoryActionID::REPO_REMOVE.asString(true)            // 2 action
306       << _sep << str::escape(repo.alias(), _sep)                        // 3 alias
307       << _sep << str::escape(ZConfig::instance().userData(), _sep)      // 4 userdata
308       << endl;
309   }
310
311
312   void HistoryLog::modifyRepository(
313       const RepoInfo & oldrepo, const RepoInfo & newrepo)
314   {
315     if (oldrepo.alias() != newrepo.alias())
316     {
317       _log
318         << timestamp()                                                  // 1 timestamp
319         << _sep << HistoryActionID::REPO_CHANGE_ALIAS.asString(true)    // 2 action
320         << _sep << str::escape(oldrepo.alias(), _sep)                   // 3 old alias
321         << _sep << str::escape(newrepo.alias(), _sep)                   // 4 new alias
322         << _sep << str::escape(ZConfig::instance().userData(), _sep)    // 5 userdata
323         << endl;
324     }
325     if (*oldrepo.baseUrlsBegin() != *newrepo.baseUrlsBegin())
326     {
327       _log
328         << timestamp()                                                  // 1 timestamp
329         << _sep << HistoryActionID::REPO_CHANGE_URL.asString(true)      // 2 action
330         << _sep << str::escape(oldrepo.alias(), _sep)                   // 3 old url
331         << _sep << *newrepo.baseUrlsBegin()                             // 4 new url
332         << _sep << str::escape(ZConfig::instance().userData(), _sep)    // 5 userdata
333         << endl;
334     }
335   }
336
337   ///////////////////////////////////////////////////////////////////
338
339 } // namespace zypp