99d670f388302260227a222de1ccd34aa45be215
[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   namespace
72   {
73     const char          _sep = '|';
74     std::ofstream       _log;
75     unsigned            _refcnt = 0;
76     Pathname            _fname;
77     Pathname            _fnameLastFail;
78
79     inline void openLog()
80     {
81       if ( _fname.empty() )
82         _fname = ZConfig::instance().historyLogFile();
83
84       _log.clear();
85       _log.open( _fname.asString().c_str(), std::ios::out|std::ios::app );
86       if( !_log && _fnameLastFail != _fname )
87       {
88         ERR << "Could not open logfile '" << _fname << "'" << endl;
89         _fnameLastFail = _fname;
90       }
91     }
92
93     inline void closeLog()
94     {
95       _log.clear();
96       _log.close();
97     }
98
99     inline void refUp()
100     {
101       if ( !_refcnt )
102         openLog();
103       ++_refcnt;
104     }
105
106     inline void refDown()
107     {
108       --_refcnt;
109       if ( !_refcnt )
110         closeLog();
111     }
112   }
113
114   ///////////////////////////////////////////////////////////////////
115   //
116   //    CLASS NAME : HistoryLog
117   //
118   ///////////////////////////////////////////////////////////////////
119
120   HistoryLog::HistoryLog( const Pathname & rootdir )
121   {
122     setRoot( rootdir );
123     refUp();
124   }
125
126   HistoryLog::~HistoryLog()
127   {
128     refDown();
129   }
130
131   void HistoryLog::setRoot( const Pathname & rootdir )
132   {
133     if ( ! rootdir.absolute() )
134       return;
135
136     if ( _refcnt )
137       closeLog();
138
139     _fname = rootdir / ZConfig::instance().historyLogFile();
140     filesystem::assert_dir( _fname.dirname() );
141     MIL << "installation log file " << _fname << endl;
142
143     if ( _refcnt )
144       openLog();
145   }
146
147   const Pathname & HistoryLog::fname()
148   {
149     if ( _fname.empty() )
150       _fname = ZConfig::instance().historyLogFile();
151     return _fname;
152   }
153
154   /////////////////////////////////////////////////////////////////////////
155
156   void HistoryLog::comment( const string & comment, bool timestamp )
157   {
158     if (comment.empty())
159       return;
160
161     _log << "# ";
162     if ( timestamp )
163       _log << ::timestamp() << " ";
164
165     const char * s = comment.c_str();
166     const char * c = s;
167     unsigned size = comment.size();
168
169     // ignore the last newline
170     if (comment[size-1] == '\n')
171       --size;
172
173     for ( unsigned i = 0; i < size; ++i, ++c )
174       if ( *c == '\n' )
175       {
176         _log << string( s, c + 1 - s ) << "# ";
177         s = c + 1;
178       }
179
180     if ( s < c )
181       _log << std::string( s, c-s );
182
183     _log << endl;
184   }
185
186   /////////////////////////////////////////////////////////////////////////
187
188   void HistoryLog::install( const PoolItem & pi )
189   {
190     const Package::constPtr p = asKind<Package>(pi.resolvable());
191     if (!p)
192       return;
193
194     _log
195       << timestamp()                                                    // 1 timestamp
196       << _sep << HistoryActionID::INSTALL.asString(true)                // 2 action
197       << _sep << p->name()                                              // 3 name
198       << _sep << p->edition()                                           // 4 evr
199       << _sep << p->arch();                                             // 5 arch
200
201     // ApplLow is what the solver selected on behalf of the user.
202     if (pi.status().isByUser() || pi.status().isByApplLow() )
203       _log << _sep << userAtHostname();                                 // 6 reqested by
204     else if (pi.status().isByApplHigh())
205       _log << _sep << pidAndAppname();
206     else
207       _log << _sep;
208
209     _log
210       << _sep << p->repoInfo().alias()                                  // 7 repo alias
211       << _sep << p->checksum().checksum()                               // 8 checksum
212       << _sep << str::escape(ZConfig::instance().userData(), _sep)      // 9 userdata
213       << endl;
214   }
215
216
217   void HistoryLog::remove( const PoolItem & pi )
218   {
219     const Package::constPtr p = asKind<Package>(pi.resolvable());
220     if (!p)
221       return;
222
223     _log
224       << timestamp()                                                    // 1 timestamp
225       << _sep << HistoryActionID::REMOVE.asString(true)                 // 2 action
226       << _sep << p->name()                                              // 3 name
227       << _sep << p->edition()                                           // 4 evr
228       << _sep << p->arch();                                             // 5 arch
229
230     // ApplLow is what the solver selected on behalf of the user.
231     if ( pi.status().isByUser() || pi.status().isByApplLow() )
232       _log << _sep << userAtHostname();                                 // 6 reqested by
233     else if (pi.status().isByApplHigh())
234       _log << _sep << pidAndAppname();
235     else
236       _log << _sep;
237
238     _log
239       << _sep << str::escape(ZConfig::instance().userData(), _sep)      // 7 userdata
240       << endl;
241   }
242
243   /////////////////////////////////////////////////////////////////////////
244
245   void HistoryLog::addRepository(const RepoInfo & repo)
246   {
247     _log
248       << timestamp()                                                    // 1 timestamp
249       << _sep << HistoryActionID::REPO_ADD.asString(true)               // 2 action
250       << _sep << str::escape(repo.alias(), _sep)                        // 3 alias
251       << _sep << str::escape(repo.url().asString(), _sep)               // 4 primary URL
252       << _sep << str::escape(ZConfig::instance().userData(), _sep)      // 5 userdata
253       << endl;
254   }
255
256
257   void HistoryLog::removeRepository(const RepoInfo & repo)
258   {
259     _log
260       << timestamp()                                                    // 1 timestamp
261       << _sep << HistoryActionID::REPO_REMOVE.asString(true)            // 2 action
262       << _sep << str::escape(repo.alias(), _sep)                        // 3 alias
263       << _sep << str::escape(ZConfig::instance().userData(), _sep)      // 4 userdata
264       << endl;
265   }
266
267
268   void HistoryLog::modifyRepository(
269       const RepoInfo & oldrepo, const RepoInfo & newrepo)
270   {
271     if (oldrepo.alias() != newrepo.alias())
272     {
273       _log
274         << timestamp()                                                  // 1 timestamp
275         << _sep << HistoryActionID::REPO_CHANGE_ALIAS.asString(true)    // 2 action
276         << _sep << str::escape(oldrepo.alias(), _sep)                   // 3 old alias
277         << _sep << str::escape(newrepo.alias(), _sep)                   // 4 new alias
278         << _sep << str::escape(ZConfig::instance().userData(), _sep)    // 5 userdata
279         << endl;
280     }
281     if ( oldrepo.url() != newrepo.url() )
282     {
283       _log
284         << timestamp()                                                  // 1 timestamp
285         << _sep << HistoryActionID::REPO_CHANGE_URL.asString(true)      // 2 action
286         << _sep << str::escape(oldrepo.url().asString(), _sep)          // 3 old url
287         << _sep << str::escape(newrepo.url().asString(), _sep)          // 4 new url
288         << _sep << str::escape(ZConfig::instance().userData(), _sep)    // 5 userdata
289         << endl;
290     }
291   }
292
293   ///////////////////////////////////////////////////////////////////
294
295 } // namespace zypp