- new History Log, first version (fate #110205)
[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
20 #include "zypp/PathInfo.h"
21 #include "zypp/Date.h"
22
23 #include "zypp/PoolItem.h"
24 #include "zypp/Package.h"
25 #include "zypp/RepoInfo.h"
26
27 #include "zypp/HistoryLog.h"
28
29 using std::endl;
30 using std::string;
31
32 namespace
33 {
34   inline string timestamp()
35   { return zypp::Date::now().form( "%Y-%m-%d %H:%M:%S" ); }
36
37   inline string userAtHostname()
38   {
39     static char buf[256];
40     string result;
41     char * tmp = ::cuserid(buf); 
42     if (tmp)
43     {
44       result = string(tmp);
45       if (!::gethostname(buf, 255))
46         result += "@" + string(buf);
47     }
48     return result;
49   }
50 }
51
52 namespace zypp
53 {
54   ///////////////////////////////////////////////////////////////////
55   //
56   //    CLASS NAME : HistoryActionID
57   //
58   ///////////////////////////////////////////////////////////////////
59
60   static std::map<std::string,HistoryActionID::ID> _table;
61
62   const HistoryActionID HistoryActionID::NONE(HistoryActionID::NONE_e);
63   const HistoryActionID HistoryActionID::INSTALL(HistoryActionID::INSTALL_e);
64   const HistoryActionID HistoryActionID::REMOVE(HistoryActionID::REMOVE_e);
65   const HistoryActionID HistoryActionID::REPO_ADD(HistoryActionID::REPO_ADD_e);
66   const HistoryActionID HistoryActionID::REPO_REMOVE(HistoryActionID::REPO_REMOVE_e);
67   const HistoryActionID HistoryActionID::REPO_CHANGE_ALIAS(HistoryActionID::REPO_CHANGE_ALIAS_e);
68   const HistoryActionID HistoryActionID::REPO_CHANGE_URL(HistoryActionID::REPO_CHANGE_URL_e);
69
70   HistoryActionID::HistoryActionID(const std::string & strval_r)
71     : _id(parse(strval_r))
72   {}
73
74   HistoryActionID::ID HistoryActionID::parse(const std::string & strval_r)
75   {
76     if (_table.empty())
77     {
78       // initialize it
79       _table["install"] = INSTALL_e;
80       _table["remove"]  = REMOVE_e;
81       _table["radd"]    = REPO_ADD_e;
82       _table["rremove"] = REPO_REMOVE_e;
83       _table["ralias"]  = REPO_CHANGE_ALIAS_e;
84       _table["rurl"]    = REPO_CHANGE_URL_e;
85       _table["NONE"] = _table["none"] = HistoryActionID::NONE_e;
86     }
87
88     std::map<std::string,HistoryActionID::ID>::const_iterator it =
89       _table.find(strval_r);
90
91     if (it == _table.end())
92       WAR << "Unknown history action ID '" + strval_r + "'";
93
94     return it->second;
95   }
96
97
98   const std::string & HistoryActionID::asString(bool pad) const
99   {
100     static std::map<ID, std::string> _table;
101     if ( _table.empty() )
102     {
103       // initialize it
104       _table[INSTALL_e]           = "install";
105       _table[REMOVE_e]            = "remove";
106       _table[REPO_ADD_e]          = "radd";
107       _table[REPO_REMOVE_e]       = "rremove";
108       _table[REPO_CHANGE_ALIAS_e] = "ralias";
109       _table[REPO_CHANGE_URL_e]   = "rurl";
110       _table[NONE_e]              = "NONE";
111     }
112     // add spaces so that the size of the returned string is always 7 (for now)
113     if (pad)
114       return _table[_id].append(7 - _table[_id].size(), ' ');
115     return _table[_id];
116   }
117
118   std::ostream & operator << (std::ostream & str, const HistoryActionID & id)
119   { return str << id.asString(); }
120
121   ///////////////////////////////////////////////////////////////////
122
123   ///////////////////////////////////////////////////////////////////
124   //
125   //    CLASS NAME : HistoryLog
126   //
127   ///////////////////////////////////////////////////////////////////
128
129     Pathname HistoryLog::_fname(ZConfig::instance().historyLogFile());
130     std::ofstream HistoryLog::_log;
131     unsigned HistoryLog::_refcnt = 0;
132     const char HistoryLog::_sep = '|';
133
134     ///////////////////////////////////////////////////////////////////
135
136     HistoryLog::HistoryLog( const Pathname & rootdir )
137     {
138       refUp();
139       if (!rootdir.empty() && rootdir.absolute())
140         _fname = rootdir / ZConfig::instance().historyLogFile();
141     }
142
143     void HistoryLog::openLog()
144     {
145       if ( !_fname.empty() )
146       {
147         _log.clear();
148         _log.open( _fname.asString().c_str(), std::ios::out|std::ios::app );
149         if( !_log )
150           ERR << "Could not open logfile '" << _fname << "'" << endl;
151       }
152     }
153
154     void HistoryLog::closeLog()
155     {
156       _log.clear();
157       _log.close();
158     }
159
160     void HistoryLog::refUp()
161     {
162       if ( !_refcnt )
163         openLog();
164       ++_refcnt;
165     }
166
167     void HistoryLog::refDown()
168     {
169       --_refcnt;
170       if ( !_refcnt )
171         closeLog();
172     }
173
174
175     void HistoryLog::setRoot( const Pathname & rootdir )
176     {
177       if (rootdir.empty() || !rootdir.absolute())
178         return;
179
180       if ( _refcnt )
181         closeLog();
182
183       _fname = rootdir / "/var/log/zypp/history";
184       filesystem::assert_dir( _fname.dirname() );
185       MIL << "installation log file " << _fname << endl;
186
187       if ( _refcnt )
188         openLog();
189     }
190
191
192     const Pathname & HistoryLog::fname()
193     { return _fname; }
194
195     /////////////////////////////////////////////////////////////////////////
196
197     void HistoryLog::comment( const string & comment, bool timestamp )
198     {
199       if (comment.empty())
200         return;
201
202       _log << "# ";
203       if ( timestamp )
204         _log << ::timestamp() << " ";
205
206       const char * s = comment.c_str();
207       const char * c = s;
208       unsigned size = comment.size();
209
210       // ignore the last newline
211       if (comment[size-1] == '\n')
212         --size;
213
214       for ( unsigned i = 0; i < size; ++i, ++c )
215         if ( *c == '\n' )
216         {
217           _log << string( s, c + 1 - s ) << "# ";
218           s = c + 1;
219         }
220
221       if ( s < c )
222         _log << std::string( s, c-s );
223
224       _log << endl;
225     }
226
227     /////////////////////////////////////////////////////////////////////////
228     
229     void HistoryLog::install( const PoolItem & pi )
230     {
231       const Package::constPtr p = asKind<Package>(pi.resolvable());
232       if (!p)
233         return;
234
235       _log
236         << timestamp()                                   // 1 timestamp
237         << _sep << HistoryActionID::INSTALL.asString(true) // 2 action
238         << _sep << p->name()                             // 3 name
239         << _sep << p->edition()                          // 4 evr
240         << _sep << p->arch();                            // 5 arch
241
242       if (pi.status().isByUser())
243         _log << _sep << userAtHostname();                // 6 reqested by
244       //else if (pi.status().isByApplHigh() || pi.status().isByApplLow())
245       //  _log << _sep << "appl";
246       else
247         _log << _sep;
248
249       _log
250         << _sep << p->repoInfo().alias()                 // 7 repo alias
251         << _sep << p->checksum().checksum();             // 8 checksum
252
253       _log << endl; 
254
255       //_log << pi << endl;
256     }
257
258
259     void HistoryLog::remove( const PoolItem & pi )
260     {
261       const Package::constPtr p = asKind<Package>(pi.resolvable());
262       if (!p)
263         return;
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       if (pi.status().isByUser())
273         _log << _sep << userAtHostname();                // 6 reqested by
274       //else if (pi.status().isByApplHigh() || pi.status().isByApplLow())
275       //  _log << _sep << "appl";
276       else
277         _log << _sep;
278
279       // we don't have checksum in rpm db
280       //  << _sep << p->checksum().checksum();           // x checksum
281
282       _log << endl; 
283
284       //_log << pi << 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 << repo.alias()                          // 3 alias
295         // what about the rest of the URLs??
296         << _sep << *repo.baseUrlsBegin()                 // 4 primary URL
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 << repo.alias()                          // 3 alias
307         << endl;
308     }
309
310
311     void HistoryLog::modifyRepository(
312         const RepoInfo & oldrepo, const RepoInfo & newrepo)
313     {
314       if (oldrepo.alias() != newrepo.alias())
315       {
316         _log
317           << timestamp()                                    // 1 timestamp
318           << _sep << HistoryActionID::REPO_CHANGE_ALIAS.asString(true) // 2 action
319           << _sep << oldrepo.alias()                        // 3 old alias
320           << _sep << newrepo.alias();                       // 4 new alias
321       }
322       
323       if (*oldrepo.baseUrlsBegin() != *newrepo.baseUrlsBegin())
324       {
325         _log
326           << timestamp()                                    // 1 timestamp
327           << _sep << HistoryActionID::REPO_CHANGE_URL.asString(true) // 2 action
328           << _sep << oldrepo.alias()                        // 3 old url
329           << _sep << newrepo.alias();                       // 4 new url
330       }
331     }
332
333     ///////////////////////////////////////////////////////////////////
334
335 } // namespace zypp