HistoryLog reader:
[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 #include "zypp/HistoryLogData.h"
29
30 using std::endl;
31 using std::string;
32
33 namespace
34 {
35   inline string timestamp()
36   { return zypp::Date::now().form( HISTORY_LOG_DATE_FORMAT ); }
37
38   inline string userAtHostname()
39   {
40     static char buf[256];
41     string result;
42     char * tmp = ::cuserid(buf);
43     if (tmp)
44     {
45       result = string(tmp);
46       if (!::gethostname(buf, 255))
47         result += "@" + string(buf);
48     }
49     return result;
50   }
51
52   static std::string pidAndAppname()
53   {
54     static std::string _val;
55     if ( _val.empty() )
56     {
57       pid_t mypid = getpid();
58       zypp::Pathname p( "/proc/"+zypp::str::numstring(mypid)+"/exe" );
59       zypp::Pathname myname( zypp::filesystem::readlink( p ) );
60
61       _val += zypp::str::numstring(mypid);
62       _val += ":";
63       _val += myname.basename();
64     }
65     return _val;
66   }
67 }
68
69 namespace zypp
70 {
71
72
73   ///////////////////////////////////////////////////////////////////
74   //
75   //    CLASS NAME : HistoryLog
76   //
77   ///////////////////////////////////////////////////////////////////
78
79   Pathname HistoryLog::_fname(ZConfig::instance().historyLogFile());
80   std::ofstream HistoryLog::_log;
81   unsigned HistoryLog::_refcnt = 0;
82   const char HistoryLog::_sep = '|';
83
84   ///////////////////////////////////////////////////////////////////
85
86   HistoryLog::HistoryLog( const Pathname & rootdir )
87   {
88     refUp();
89     if (!rootdir.empty() && rootdir.absolute())
90       _fname = rootdir / ZConfig::instance().historyLogFile();
91   }
92
93   void HistoryLog::openLog()
94   {
95     if ( !_fname.empty() )
96     {
97       _log.clear();
98       _log.open( _fname.asString().c_str(), std::ios::out|std::ios::app );
99       if( !_log )
100         ERR << "Could not open logfile '" << _fname << "'" << endl;
101     }
102   }
103
104   void HistoryLog::closeLog()
105   {
106     _log.clear();
107     _log.close();
108   }
109
110   void HistoryLog::refUp()
111   {
112     if ( !_refcnt )
113       openLog();
114     ++_refcnt;
115   }
116
117   void HistoryLog::refDown()
118   {
119     --_refcnt;
120     if ( !_refcnt )
121       closeLog();
122   }
123
124
125   void HistoryLog::setRoot( const Pathname & rootdir )
126   {
127     if (rootdir.empty() || !rootdir.absolute())
128       return;
129
130     if ( _refcnt )
131       closeLog();
132
133     _fname = rootdir / "/var/log/zypp/history";
134     filesystem::assert_dir( _fname.dirname() );
135     MIL << "installation log file " << _fname << endl;
136
137     if ( _refcnt )
138       openLog();
139   }
140
141
142   const Pathname & HistoryLog::fname()
143   { return _fname; }
144
145   /////////////////////////////////////////////////////////////////////////
146
147   void HistoryLog::comment( const string & comment, bool timestamp )
148   {
149     if (comment.empty())
150       return;
151
152     _log << "# ";
153     if ( timestamp )
154       _log << ::timestamp() << " ";
155
156     const char * s = comment.c_str();
157     const char * c = s;
158     unsigned size = comment.size();
159
160     // ignore the last newline
161     if (comment[size-1] == '\n')
162       --size;
163
164     for ( unsigned i = 0; i < size; ++i, ++c )
165       if ( *c == '\n' )
166       {
167         _log << string( s, c + 1 - s ) << "# ";
168         s = c + 1;
169       }
170
171     if ( s < c )
172       _log << std::string( s, c-s );
173
174     _log << endl;
175   }
176
177   /////////////////////////////////////////////////////////////////////////
178
179   void HistoryLog::install( const PoolItem & pi )
180   {
181     const Package::constPtr p = asKind<Package>(pi.resolvable());
182     if (!p)
183       return;
184
185     _log
186       << timestamp()                                   // 1 timestamp
187       << _sep << HistoryActionID::INSTALL.asString(true) // 2 action
188       << _sep << p->name()                             // 3 name
189       << _sep << p->edition()                          // 4 evr
190       << _sep << p->arch();                            // 5 arch
191
192     // ApplLow is what the solver selected on behalf of the user.
193     if (pi.status().isByUser() || pi.status().isByApplLow() )
194       _log << _sep << userAtHostname();                // 6 reqested by
195     else if (pi.status().isByApplHigh())
196       _log << _sep << pidAndAppname();
197     else
198       _log << _sep;
199
200     _log
201       << _sep << p->repoInfo().alias()                 // 7 repo alias
202       << _sep << p->checksum().checksum();             // 8 checksum
203
204     _log << endl;
205
206     //_log << pi << endl;
207   }
208
209
210   void HistoryLog::remove( const PoolItem & pi )
211   {
212     const Package::constPtr p = asKind<Package>(pi.resolvable());
213     if (!p)
214       return;
215
216     _log
217       << timestamp()                                   // 1 timestamp
218       << _sep << HistoryActionID::REMOVE.asString(true) // 2 action
219       << _sep << p->name()                             // 3 name
220       << _sep << p->edition()                          // 4 evr
221       << _sep << p->arch();                            // 5 arch
222
223     // ApplLow is what the solver selected on behalf of the user.
224     if ( pi.status().isByUser() || pi.status().isByApplLow() )
225       _log << _sep << userAtHostname();                // 6 reqested by
226     else if (pi.status().isByApplHigh())
227       _log << _sep << pidAndAppname();
228     else
229       _log << _sep;
230
231     // we don't have checksum in rpm db
232     //  << _sep << p->checksum().checksum();           // x checksum
233
234     _log << endl;
235
236     //_log << pi << endl;
237   }
238
239   /////////////////////////////////////////////////////////////////////////
240
241   void HistoryLog::addRepository(const RepoInfo & repo)
242   {
243     _log
244       << timestamp()                                   // 1 timestamp
245       << _sep << HistoryActionID::REPO_ADD.asString(true) // 2 action
246       << _sep << str::escape(repo.alias(), _sep)       // 3 alias
247       // what about the rest of the URLs??
248       << _sep << *repo.baseUrlsBegin()                 // 4 primary URL
249       << endl;
250   }
251
252
253   void HistoryLog::removeRepository(const RepoInfo & repo)
254   {
255     _log
256       << timestamp()                                   // 1 timestamp
257       << _sep << HistoryActionID::REPO_REMOVE.asString(true) // 2 action
258       << _sep << str::escape(repo.alias(), _sep)       // 3 alias
259       << endl;
260   }
261
262
263   void HistoryLog::modifyRepository(
264       const RepoInfo & oldrepo, const RepoInfo & newrepo)
265   {
266     if (oldrepo.alias() != newrepo.alias())
267     {
268       _log
269         << timestamp()                                    // 1 timestamp
270         << _sep << HistoryActionID::REPO_CHANGE_ALIAS.asString(true) // 2 action
271         << _sep << str::escape(oldrepo.alias(), _sep)     // 3 old alias
272         << _sep << str::escape(newrepo.alias(), _sep)     // 4 new alias
273         << endl;
274     }
275
276     if (*oldrepo.baseUrlsBegin() != *newrepo.baseUrlsBegin())
277     {
278       _log
279         << timestamp()                                             //1 timestamp
280         << _sep << HistoryActionID::REPO_CHANGE_URL.asString(true) // 2 action
281         << _sep << str::escape(oldrepo.alias(), _sep)              // 3 old url
282         << _sep << *newrepo.baseUrlsBegin()                        // 4 new url
283         << endl;
284     }
285   }
286
287   ///////////////////////////////////////////////////////////////////
288
289 } // namespace zypp