1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/HistoryLog.cc
16 #include <zypp/ZConfig.h>
17 #include <zypp/base/String.h>
18 #include <zypp/base/Logger.h>
19 #include <zypp/base/IOStream.h>
21 #include <zypp/PathInfo.h>
22 #include <zypp/Date.h>
24 #include <zypp/PoolItem.h>
25 #include <zypp/Package.h>
26 #include <zypp/RepoInfo.h>
28 #include <zypp/HistoryLog.h>
29 #include <zypp/HistoryLogData.h>
38 inline string timestamp()
39 { return zypp::Date::now().form( HISTORY_LOG_DATE_FORMAT ); }
41 inline string userAtHostname()
45 char * tmp = ::cuserid(buf);
49 if (!::gethostname(buf, 255))
50 result += "@" + string(buf);
55 static std::string pidAndAppname()
57 static std::string _val;
60 pid_t mypid = getpid();
61 zypp::Pathname p( "/proc/"+zypp::str::numstring(mypid)+"/exe" );
62 zypp::Pathname myname( zypp::filesystem::readlink( p ) );
64 _val += zypp::str::numstring(mypid);
66 _val += myname.basename();
71 static std::string cmdline()
73 static std::string _val;
76 pid_t mypid = getpid();
78 std::ifstream cmdlineStr( Pathname("/proc/"+zypp::str::numstring(mypid)+"/cmdline").c_str() );
80 const char * sep = "'";
81 while ( cmdlineStr && cmdlineStr.get( ch ) )
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;
105 const char _sep = '|';
107 unsigned _refcnt = 0;
109 Pathname _fnameLastFail;
111 inline void openLog()
113 if ( _fname.empty() )
114 _fname = ZConfig::instance().historyLogFile();
117 _log.open( _fname.asString().c_str(), std::ios::out|std::ios::app );
118 if( !_log && _fnameLastFail != _fname )
120 ERR << "Could not open logfile '" << _fname << "'" << endl;
121 _fnameLastFail = _fname;
125 inline void closeLog()
138 inline void refDown()
146 ///////////////////////////////////////////////////////////////////
148 // CLASS NAME : HistoryLog
150 ///////////////////////////////////////////////////////////////////
152 HistoryLog::HistoryLog( const Pathname & rootdir )
158 HistoryLog::~HistoryLog()
163 void HistoryLog::setRoot( const Pathname & rootdir )
165 if ( ! rootdir.absolute() )
171 _fname = rootdir / ZConfig::instance().historyLogFile();
172 filesystem::assert_dir( _fname.dirname() );
173 MIL << "installation log file " << _fname << endl;
179 const Pathname & HistoryLog::fname()
181 if ( _fname.empty() )
182 _fname = ZConfig::instance().historyLogFile();
186 /////////////////////////////////////////////////////////////////////////
188 void HistoryLog::comment( const string & comment, bool timestamp_r )
195 _log << timestamp() << " ";
197 const char * s = comment.c_str();
199 unsigned size = comment.size();
201 // ignore the last newline
202 if (comment[size-1] == '\n')
205 for ( unsigned i = 0; i < size; ++i, ++c )
208 _log << string( s, c + 1 - s ) << "# ";
213 _log << std::string( s, c-s );
218 /////////////////////////////////////////////////////////////////////////
220 void HistoryLog::stampCommand()
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
232 void HistoryLog::install( const PoolItem & pi )
234 if ( ! pi.isKind<Package>() ) return;
235 const Package::constPtr p = asKind<Package>(pi.resolvable());
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
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();
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
260 void HistoryLog::remove( const PoolItem & pi )
262 if ( ! pi.isKind<Package>() ) return;
263 const Package::constPtr p = asKind<Package>(pi.resolvable());
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
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();
281 << _sep << str::escape(ZConfig::instance().userData(), _sep) // 7 userdata
285 /////////////////////////////////////////////////////////////////////////
287 void HistoryLog::addRepository(const RepoInfo & repo)
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
299 void HistoryLog::removeRepository(const RepoInfo & repo)
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
310 void HistoryLog::modifyRepository(
311 const RepoInfo & oldrepo, const RepoInfo & newrepo)
313 if (oldrepo.alias() != newrepo.alias())
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
323 if ( oldrepo.url() != newrepo.url() )
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
335 void HistoryLog::patchStateChange( const PoolItem & pi, ResStatus::ValidateValue oldstate )
337 if ( ! pi.isKind<Patch>() ) return;
338 const Patch::constPtr p = asKind<Patch>(pi.resolvable());
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
355 ///////////////////////////////////////////////////////////////////