#include "zypp/ZConfig.h"
#include "zypp/base/String.h"
#include "zypp/base/Logger.h"
+#include "zypp/base/IOStream.h"
#include "zypp/PathInfo.h"
#include "zypp/Date.h"
#include "zypp/RepoInfo.h"
#include "zypp/HistoryLog.h"
+#include "zypp/HistoryLogData.h"
using std::endl;
using std::string;
-namespace
-{
- inline string timestamp()
- { return zypp::Date::now().form( "%Y-%m-%d %H:%M:%S" ); }
-
- inline string userAtHostname()
- {
- static char buf[256];
- string result;
- char * tmp = ::cuserid(buf);
- if (tmp)
- {
- result = string(tmp);
- if (!::gethostname(buf, 255))
- result += "@" + string(buf);
- }
- return result;
- }
-}
-
namespace zypp
{
- ///////////////////////////////////////////////////////////////////
- //
- // CLASS NAME : HistoryActionID
- //
- ///////////////////////////////////////////////////////////////////
-
- static std::map<std::string,HistoryActionID::ID> _table;
-
- const HistoryActionID HistoryActionID::NONE(HistoryActionID::NONE_e);
- const HistoryActionID HistoryActionID::INSTALL(HistoryActionID::INSTALL_e);
- const HistoryActionID HistoryActionID::REMOVE(HistoryActionID::REMOVE_e);
- const HistoryActionID HistoryActionID::REPO_ADD(HistoryActionID::REPO_ADD_e);
- const HistoryActionID HistoryActionID::REPO_REMOVE(HistoryActionID::REPO_REMOVE_e);
- const HistoryActionID HistoryActionID::REPO_CHANGE_ALIAS(HistoryActionID::REPO_CHANGE_ALIAS_e);
- const HistoryActionID HistoryActionID::REPO_CHANGE_URL(HistoryActionID::REPO_CHANGE_URL_e);
-
- HistoryActionID::HistoryActionID(const std::string & strval_r)
- : _id(parse(strval_r))
- {}
-
- HistoryActionID::ID HistoryActionID::parse(const std::string & strval_r)
+ namespace
{
- if (_table.empty())
+ inline string timestamp()
+ { return zypp::Date::now().form( HISTORY_LOG_DATE_FORMAT ); }
+
+ inline string userAtHostname()
{
- // initialize it
- _table["install"] = INSTALL_e;
- _table["remove"] = REMOVE_e;
- _table["radd"] = REPO_ADD_e;
- _table["rremove"] = REPO_REMOVE_e;
- _table["ralias"] = REPO_CHANGE_ALIAS_e;
- _table["rurl"] = REPO_CHANGE_URL_e;
- _table["NONE"] = _table["none"] = HistoryActionID::NONE_e;
+ static char buf[256];
+ string result;
+ char * tmp = ::cuserid(buf);
+ if (tmp)
+ {
+ result = string(tmp);
+ if (!::gethostname(buf, 255))
+ result += "@" + string(buf);
+ }
+ return result;
}
- std::map<std::string,HistoryActionID::ID>::const_iterator it =
- _table.find(strval_r);
-
- if (it == _table.end())
- WAR << "Unknown history action ID '" + strval_r + "'";
-
- return it->second;
- }
+ static std::string pidAndAppname()
+ {
+ static std::string _val;
+ if ( _val.empty() )
+ {
+ pid_t mypid = getpid();
+ zypp::Pathname p( "/proc/"+zypp::str::numstring(mypid)+"/exe" );
+ zypp::Pathname myname( zypp::filesystem::readlink( p ) );
+ _val += zypp::str::numstring(mypid);
+ _val += ":";
+ _val += myname.basename();
+ }
+ return _val;
+ }
- const std::string & HistoryActionID::asString(bool pad) const
- {
- static std::map<ID, std::string> _table;
- if ( _table.empty() )
+ static std::string cmdline()
{
- // initialize it
- _table[INSTALL_e] = "install";
- _table[REMOVE_e] = "remove";
- _table[REPO_ADD_e] = "radd";
- _table[REPO_REMOVE_e] = "rremove";
- _table[REPO_CHANGE_ALIAS_e] = "ralias";
- _table[REPO_CHANGE_URL_e] = "rurl";
- _table[NONE_e] = "NONE";
+ static std::string _val;
+ if ( _val.empty() )
+ {
+ pid_t mypid = getpid();
+ {
+ std::ifstream cmdlineStr( Pathname("/proc/"+zypp::str::numstring(mypid)+"/cmdline").c_str() );
+ char ch;
+ const char * sep = "'";
+ while ( cmdlineStr && cmdlineStr.get( ch ) )
+ {
+ if ( sep )
+ {
+ _val += sep;
+ sep = nullptr;
+ }
+ switch ( ch )
+ {
+ case '\0': _val += '\''; sep = " '"; break;
+ case '\n': _val += ' '; break;
+ case '\\': _val += '\\'; _val += '\\'; break;
+ case '|': _val += '\\'; _val += '|'; break;
+ default: _val += ch; break;
+ }
+ }
+ }
+ }
+ return _val;
}
- // add spaces so that the size of the returned string is always 7 (for now)
- if (pad)
- return _table[_id].append(7 - _table[_id].size(), ' ');
- return _table[_id];
- }
-
- std::ostream & operator << (std::ostream & str, const HistoryActionID & id)
- { return str << id.asString(); }
-
- ///////////////////////////////////////////////////////////////////
-
- ///////////////////////////////////////////////////////////////////
- //
- // CLASS NAME : HistoryLog
- //
- ///////////////////////////////////////////////////////////////////
-
- Pathname HistoryLog::_fname(ZConfig::instance().historyLogFile());
- std::ofstream HistoryLog::_log;
- unsigned HistoryLog::_refcnt = 0;
- const char HistoryLog::_sep = '|';
+ } // namespace
- ///////////////////////////////////////////////////////////////////
+ namespace
+ {
+ const char _sep = '|';
+ std::ofstream _log;
+ unsigned _refcnt = 0;
+ Pathname _fname;
+ Pathname _fnameLastFail;
- HistoryLog::HistoryLog( const Pathname & rootdir )
+ inline void openLog()
{
- refUp();
- if (!rootdir.empty() && rootdir.absolute())
- _fname = rootdir / ZConfig::instance().historyLogFile();
- }
+ if ( _fname.empty() )
+ _fname = ZConfig::instance().historyLogFile();
- void HistoryLog::openLog()
- {
- if ( !_fname.empty() )
+ _log.clear();
+ _log.open( _fname.asString().c_str(), std::ios::out|std::ios::app );
+ if( !_log && _fnameLastFail != _fname )
{
- _log.clear();
- _log.open( _fname.asString().c_str(), std::ios::out|std::ios::app );
- if( !_log )
- ERR << "Could not open logfile '" << _fname << "'" << endl;
+ ERR << "Could not open logfile '" << _fname << "'" << endl;
+ _fnameLastFail = _fname;
}
}
- void HistoryLog::closeLog()
+ inline void closeLog()
{
_log.clear();
_log.close();
}
- void HistoryLog::refUp()
+ inline void refUp()
{
if ( !_refcnt )
openLog();
++_refcnt;
}
- void HistoryLog::refDown()
+ inline void refDown()
{
--_refcnt;
if ( !_refcnt )
closeLog();
}
+ } // namespace
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : HistoryLog
+ //
+ ///////////////////////////////////////////////////////////////////
- void HistoryLog::setRoot( const Pathname & rootdir )
- {
- if (rootdir.empty() || !rootdir.absolute())
- return;
-
- if ( _refcnt )
- closeLog();
+ HistoryLog::HistoryLog( const Pathname & rootdir )
+ {
+ setRoot( rootdir );
+ refUp();
+ }
- _fname = rootdir / "/var/log/zypp/history";
- filesystem::assert_dir( _fname.dirname() );
- MIL << "installation log file " << _fname << endl;
+ HistoryLog::~HistoryLog()
+ {
+ refDown();
+ }
- if ( _refcnt )
- openLog();
- }
+ void HistoryLog::setRoot( const Pathname & rootdir )
+ {
+ if ( ! rootdir.absolute() )
+ return;
+ if ( _refcnt )
+ closeLog();
- const Pathname & HistoryLog::fname()
- { return _fname; }
+ _fname = rootdir / ZConfig::instance().historyLogFile();
+ filesystem::assert_dir( _fname.dirname() );
+ MIL << "installation log file " << _fname << endl;
- /////////////////////////////////////////////////////////////////////////
+ if ( _refcnt )
+ openLog();
+ }
- void HistoryLog::comment( const string & comment, bool timestamp )
- {
- if (comment.empty())
- return;
+ const Pathname & HistoryLog::fname()
+ {
+ if ( _fname.empty() )
+ _fname = ZConfig::instance().historyLogFile();
+ return _fname;
+ }
- _log << "# ";
- if ( timestamp )
- _log << ::timestamp() << " ";
+ /////////////////////////////////////////////////////////////////////////
- const char * s = comment.c_str();
- const char * c = s;
- unsigned size = comment.size();
+ void HistoryLog::comment( const string & comment, bool timestamp_r )
+ {
+ if (comment.empty())
+ return;
- // ignore the last newline
- if (comment[size-1] == '\n')
- --size;
+ _log << "# ";
+ if ( timestamp_r )
+ _log << timestamp() << " ";
- for ( unsigned i = 0; i < size; ++i, ++c )
- if ( *c == '\n' )
- {
- _log << string( s, c + 1 - s ) << "# ";
- s = c + 1;
- }
+ const char * s = comment.c_str();
+ const char * c = s;
+ unsigned size = comment.size();
- if ( s < c )
- _log << std::string( s, c-s );
+ // ignore the last newline
+ if (comment[size-1] == '\n')
+ --size;
- _log << endl;
- }
+ for ( unsigned i = 0; i < size; ++i, ++c )
+ if ( *c == '\n' )
+ {
+ _log << string( s, c + 1 - s ) << "# ";
+ s = c + 1;
+ }
- /////////////////////////////////////////////////////////////////////////
-
- void HistoryLog::install( const PoolItem & pi )
- {
- const Package::constPtr p = asKind<Package>(pi.resolvable());
- if (!p)
- return;
+ if ( s < c )
+ _log << std::string( s, c-s );
- _log
- << timestamp() // 1 timestamp
- << _sep << HistoryActionID::INSTALL.asString(true) // 2 action
- << _sep << p->name() // 3 name
- << _sep << p->edition() // 4 evr
- << _sep << p->arch(); // 5 arch
-
- if (pi.status().isByUser())
- _log << _sep << userAtHostname(); // 6 reqested by
- //else if (pi.status().isByApplHigh() || pi.status().isByApplLow())
- // _log << _sep << "appl";
- else
- _log << _sep;
+ _log << endl;
+ }
- _log
- << _sep << p->repoInfo().alias() // 7 repo alias
- << _sep << p->checksum().checksum(); // 8 checksum
+ /////////////////////////////////////////////////////////////////////////
- _log << endl;
+ void HistoryLog::stampCommand()
+ {
+ _log
+ << timestamp() // 1 timestamp
+ << _sep << HistoryActionID::STAMP_COMMAND.asString(true) // 2 action
+ << _sep << userAtHostname() // 3 requested by
+ << _sep << cmdline() // 4 command
+ << _sep << str::escape(ZConfig::instance().userData(), _sep) // 6 userdata
+ << endl;
- //_log << pi << endl;
- }
+ }
+ void HistoryLog::install( const PoolItem & pi )
+ {
+ const Package::constPtr p = asKind<Package>(pi.resolvable());
+ if (!p)
+ return;
+
+ _log
+ << timestamp() // 1 timestamp
+ << _sep << HistoryActionID::INSTALL.asString(true) // 2 action
+ << _sep << p->name() // 3 name
+ << _sep << p->edition() // 4 evr
+ << _sep << p->arch(); // 5 arch
+
+ // ApplLow is what the solver selected on behalf of the user.
+ if (pi.status().isByUser() || pi.status().isByApplLow() )
+ _log << _sep << userAtHostname(); // 6 requested by
+ else if (pi.status().isByApplHigh())
+ _log << _sep << pidAndAppname();
+ else
+ _log << _sep;
+
+ _log
+ << _sep << p->repoInfo().alias() // 7 repo alias
+ << _sep << p->checksum().checksum() // 8 checksum
+ << _sep << str::escape(ZConfig::instance().userData(), _sep) // 9 userdata
+ << endl;
+ }
- void HistoryLog::remove( const PoolItem & pi )
- {
- const Package::constPtr p = asKind<Package>(pi.resolvable());
- if (!p)
- return;
- _log
- << timestamp() // 1 timestamp
- << _sep << HistoryActionID::REMOVE.asString(true) // 2 action
- << _sep << p->name() // 3 name
- << _sep << p->edition() // 4 evr
- << _sep << p->arch(); // 5 arch
+ void HistoryLog::remove( const PoolItem & pi )
+ {
+ const Package::constPtr p = asKind<Package>(pi.resolvable());
+ if (!p)
+ return;
+
+ _log
+ << timestamp() // 1 timestamp
+ << _sep << HistoryActionID::REMOVE.asString(true) // 2 action
+ << _sep << p->name() // 3 name
+ << _sep << p->edition() // 4 evr
+ << _sep << p->arch(); // 5 arch
+
+ // ApplLow is what the solver selected on behalf of the user.
+ if ( pi.status().isByUser() || pi.status().isByApplLow() )
+ _log << _sep << userAtHostname(); // 6 requested by
+ else if (pi.status().isByApplHigh())
+ _log << _sep << pidAndAppname();
+ else
+ _log << _sep;
+
+ _log
+ << _sep << str::escape(ZConfig::instance().userData(), _sep) // 7 userdata
+ << endl;
+ }
- if (pi.status().isByUser())
- _log << _sep << userAtHostname(); // 6 reqested by
- //else if (pi.status().isByApplHigh() || pi.status().isByApplLow())
- // _log << _sep << "appl";
- else
- _log << _sep;
+ /////////////////////////////////////////////////////////////////////////
- // we don't have checksum in rpm db
- // << _sep << p->checksum().checksum(); // x checksum
+ void HistoryLog::addRepository(const RepoInfo & repo)
+ {
+ _log
+ << timestamp() // 1 timestamp
+ << _sep << HistoryActionID::REPO_ADD.asString(true) // 2 action
+ << _sep << str::escape(repo.alias(), _sep) // 3 alias
+ << _sep << str::escape(repo.url().asString(), _sep) // 4 primary URL
+ << _sep << str::escape(ZConfig::instance().userData(), _sep) // 5 userdata
+ << endl;
+ }
- _log << endl;
- //_log << pi << endl;
- }
+ void HistoryLog::removeRepository(const RepoInfo & repo)
+ {
+ _log
+ << timestamp() // 1 timestamp
+ << _sep << HistoryActionID::REPO_REMOVE.asString(true) // 2 action
+ << _sep << str::escape(repo.alias(), _sep) // 3 alias
+ << _sep << str::escape(ZConfig::instance().userData(), _sep) // 4 userdata
+ << endl;
+ }
- /////////////////////////////////////////////////////////////////////////
- void HistoryLog::addRepository(const RepoInfo & repo)
+ void HistoryLog::modifyRepository(
+ const RepoInfo & oldrepo, const RepoInfo & newrepo)
+ {
+ if (oldrepo.alias() != newrepo.alias())
{
_log
- << timestamp() // 1 timestamp
- << _sep << HistoryActionID::REPO_ADD.asString(true) // 2 action
- << _sep << repo.alias() // 3 alias
- // what about the rest of the URLs??
- << _sep << *repo.baseUrlsBegin() // 4 primary URL
+ << timestamp() // 1 timestamp
+ << _sep << HistoryActionID::REPO_CHANGE_ALIAS.asString(true) // 2 action
+ << _sep << str::escape(oldrepo.alias(), _sep) // 3 old alias
+ << _sep << str::escape(newrepo.alias(), _sep) // 4 new alias
+ << _sep << str::escape(ZConfig::instance().userData(), _sep) // 5 userdata
<< endl;
}
-
-
- void HistoryLog::removeRepository(const RepoInfo & repo)
+ if ( oldrepo.url() != newrepo.url() )
{
_log
- << timestamp() // 1 timestamp
- << _sep << HistoryActionID::REPO_REMOVE.asString(true) // 2 action
- << _sep << repo.alias() // 3 alias
+ << timestamp() // 1 timestamp
+ << _sep << HistoryActionID::REPO_CHANGE_URL.asString(true) // 2 action
+ << _sep << str::escape(oldrepo.url().asString(), _sep) // 3 old url
+ << _sep << str::escape(newrepo.url().asString(), _sep) // 4 new url
+ << _sep << str::escape(ZConfig::instance().userData(), _sep) // 5 userdata
<< endl;
}
+ }
-
- void HistoryLog::modifyRepository(
- const RepoInfo & oldrepo, const RepoInfo & newrepo)
- {
- if (oldrepo.alias() != newrepo.alias())
- {
- _log
- << timestamp() // 1 timestamp
- << _sep << HistoryActionID::REPO_CHANGE_ALIAS.asString(true) // 2 action
- << _sep << oldrepo.alias() // 3 old alias
- << _sep << newrepo.alias(); // 4 new alias
- }
-
- if (*oldrepo.baseUrlsBegin() != *newrepo.baseUrlsBegin())
- {
- _log
- << timestamp() // 1 timestamp
- << _sep << HistoryActionID::REPO_CHANGE_URL.asString(true) // 2 action
- << _sep << oldrepo.alias() // 3 old url
- << _sep << newrepo.alias(); // 4 new url
- }
- }
-
- ///////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////
} // namespace zypp