ref: refs/heads/ma-misc
commit 823f619e7037a77e68842136c9fc4645b4cbcc52
Author: Michael Andres
Date: Thu Aug 6 18:25:55 2009 +0200
Add class CheckAccessDeleted and command zypp-CheckAccessDeleted.
- Provide class CheckAccessDeleted and command zypp-CheckAccessDeleted
to check for running processes which access meanwhile deleted files or
libraries. This may be used after commit, when trying to figure out
which services need to be restated. (fate #300763).
---
VERSION.cmake | 4 +-
libzypp.spec.cmake | 5 +-
package/libzypp.changes | 9 ++
tools/CMakeLists.txt | 2 +
tools/zypp-CheckAccessDeleted.cc | 139 ++++++++++++++++++++++++++++
zypp/misc/CheckAccessDeleted.cc | 186 +++++++++++++++++++++++++++++++++++++-
zypp/misc/CheckAccessDeleted.h | 87 +++++++++++++++---
7 files changed, 409 insertions(+), 23 deletions(-)
diff --git a/VERSION.cmake b/VERSION.cmake
index fb65779..933719f 100644
--- a/VERSION.cmake
+++ b/VERSION.cmake
@@ -61,8 +61,8 @@
SET(LIBZYPP_MAJOR "6")
SET(LIBZYPP_COMPATMINOR "11")
SET(LIBZYPP_MINOR "13")
-SET(LIBZYPP_PATCH "0")
+SET(LIBZYPP_PATCH "1")
#
-# LAST RELEASED: 6.13.0 (11)
+# LAST RELEASED: 6.13.1 (11)
# (The number in parenthesis is LIBZYPP_COMPATMINOR)
#=======
diff --git a/libzypp.spec.cmake b/libzypp.spec.cmake
index 4df556b..3e8d2b8 100644
--- a/libzypp.spec.cmake
+++ b/libzypp.spec.cmake
@@ -25,7 +25,7 @@ Provides: yast2-packagemanager
Obsoletes: yast2-packagemanager
Recommends: logrotate
BuildRequires: cmake
-BuildRequires: libsatsolver-devel >= 0.14.4
+BuildRequires: libsatsolver-devel >= 0.14.4
BuildRequires: openssl-devel
BuildRequires: boost-devel curl-devel dejagnu doxygen gcc-c++ gettext-devel graphviz hal-devel libxml2-devel
@@ -136,6 +136,7 @@ make install DESTDIR=$RPM_BUILD_ROOT
make -C doc/autodoc install DESTDIR=$RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/etc/zypp/repos.d
mkdir -p $RPM_BUILD_ROOT/etc/zypp/services.d
+mkdir -p $RPM_BUILD_ROOT/%{_usr}/lib/zypp/bin
mkdir -p $RPM_BUILD_ROOT/%{_var}/lib/zypp
mkdir -p $RPM_BUILD_ROOT/%{_var}/log/zypp
mkdir -p $RPM_BUILD_ROOT/%{_var}/cache/zypp
@@ -153,7 +154,6 @@ cd ..
%post
%run_ldconfig
if [ -f /var/cache/zypp/zypp.db ]; then rm /var/cache/zypp/zypp.db; fi
-#%{prefix}/lib/zypp/zypp-migrate-sources
# convert old lock file to new
# TODO make this a separate file?
@@ -233,6 +233,7 @@ rm -rf "$RPM_BUILD_ROOT"
%dir %{_var}/lib/zypp
%dir %{_var}/log/zypp
%dir %{_var}/cache/zypp
+%{prefix}/lib/zypp
%{prefix}/share/zypp
%{prefix}/share/applications/package-manager.desktop
%{prefix}/share/icons/hicolor/scalable/apps/package-manager-icon.svg
diff --git a/package/libzypp.changes b/package/libzypp.changes
index f784f67..3247faf 100644
--- a/package/libzypp.changes
+++ b/package/libzypp.changes
@@ -1,4 +1,13 @@
-------------------------------------------------------------------
+Thu Aug 6 18:18:21 CEST 2009 - ma@suse.de
+
+- Provide class CheckAccessDeleted and command zypp-CheckAccessDeleted
+ to check for running processes which access meanwhile deleted files or
+ libraries. This may be used after commit, when trying to figure out
+ which services need to be restated. (fate #300763).
+- version 6.13.1 (11)
+
+-------------------------------------------------------------------
Mon Aug 3 18:46:13 CEST 2009 - ma@suse.de
- New Resolver::addUpgradeRepo to perform a dist upgrade restricted to
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index cd81927..3f2255f 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -1,6 +1,8 @@
ADD_SUBDIRECTORY( package-manager )
+INSTALL(PROGRAMS zypp-CheckAccessDeleted DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
+
## ############################################################
FILE( GLOB ALLCC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc" )
diff --git a/tools/zypp-CheckAccessDeleted.cc b/tools/zypp-CheckAccessDeleted.cc
new file mode 100644
index 0000000..9b43c77
--- /dev/null
+++ b/tools/zypp-CheckAccessDeleted.cc
@@ -0,0 +1,139 @@
+#include <iostream>
+#include
+#include
+#include
+#include
+#include
+
+/** Collect max string size. */
+struct TableCol
+{
+ TableCol( size_t size_r = 0 )
+ : size( size_r )
+ {}
+ TableCol( const std::string & header_r )
+ : header( header_r ), size( header_r.size() )
+ {}
+ void operator()( const std::string & val_r )
+ { if ( val_r.size() > size ) size = val_r.size(); }
+
+ std::string header;
+ size_t size;
+};
+
+/** Scan to determine column sizes, then print. */
+struct ProcInfoTable
+{
+ ProcInfoTable()
+ : pid ( "PID" )
+ , ppid ( "PPID" )
+ , puid ( "UID" )
+ , login ( "LOGIN" )
+ , command ( "COMMAND" )
+ , service ( "SERVICE" )
+ , files ( "FILES" )
+ {}
+
+ void scan( const zypp::CheckAccessDeleted::ProcInfo & val_r )
+ {
+ pid( val_r.pid );
+ ppid( val_r.ppid );
+ puid( val_r.puid );
+ login( val_r.login );
+ command( val_r.command );
+ service( val_r.service() );
+ }
+
+ void printHeader() const
+ {
+ printRow( pid.header,
+ ppid.header,
+ puid.header,
+ login.header,
+ command.header,
+ service.header,
+ files.header );
+ }
+
+ void print( const zypp::CheckAccessDeleted::ProcInfo & val_r ) const
+ {
+ printRow( val_r.pid,
+ val_r.ppid,
+ val_r.puid,
+ val_r.login,
+ val_r.command,
+ val_r.service(),
+ zypp::str::join( val_r.files, ", " ) );
+ }
+
+ void printRow( const std::string & pid_r,
+ const std::string & ppid_r,
+ const std::string & puid_r,
+ const std::string & login_r,
+ const std::string & command_r,
+ const std::string & service_r,
+ const std::string & files_r ) const
+ {
+ printf( "%*s %*s %*s %-*s %-*s %-*s %-s\n",
+ pid.size, pid_r.c_str(),
+ ppid.size, ppid_r.c_str(),
+ puid.size, puid_r.c_str(),
+ login.size, login_r.c_str(),
+ command.size, command_r.c_str(),
+ service.size, (service_r.empty() ? " -" : service_r.c_str()),
+ files_r.c_str() );
+ }
+
+ TableCol pid;
+ TableCol ppid;
+ TableCol puid;
+ TableCol login;
+ TableCol command;
+ TableCol service;
+ TableCol files;
+};
+
+int main( int argc, char * argv[] )
+{
+ if ( argc >= 2 )
+ {
+ std::string progname( zypp::Pathname::basename( argv[0] ) );
+ if ( strcmp( argv[1], "--help" ) == 0 )
+ {
+ std::cout << "Usage: " << progname << " [--help]" << std::endl;
+ std::cout << "List information about all running processe" << std::endl;
+ std::cout << "which access deleted files or libraries." << std::endl;
+ std::cout << " PID " << "process ID" << std::endl;
+ std::cout << " PPID " << "parent process ID" << std::endl;
+ std::cout << " UID " << "process user ID" << std::endl;
+ std::cout << " LOGIN " << "process login name" << std::endl;
+ std::cout << " COMMAND " << "process command name" << std::endl;
+ std::cout << " SERVICE " << "/etc/init.d/ script that might be used to restart the command (guessed)" << std::endl;
+ std::cout << " FILES " << "list of deleted files or libraries accessed" << std::endl;
+ return 0;
+ }
+ std::cerr << progname << ": unexpected argument '" << argv[1] << "'" << std::endl;
+ std::cerr << "Try `" << progname << " --help' for more information." << std::endl;
+ return 1;
+ }
+
+ zypp::CheckAccessDeleted checker(false); // wait for explicit call to check()
+ try {
+ checker.check();
+ }
+ catch( const zypp::Exception & err )
+ {
+ std::cerr << err << std::endl << err.historyAsString();
+ return 2;
+ }
+
+ ProcInfoTable table;
+ for_( it, checker.begin(), checker.end() )
+ table.scan( *it );
+
+ table.printHeader();
+ for_( it, checker.begin(), checker.end() )
+ table.print( *it );
+
+ return 0;
+}
diff --git a/zypp/misc/CheckAccessDeleted.cc b/zypp/misc/CheckAccessDeleted.cc
index 678d7ea..b0f0275 100644
--- a/zypp/misc/CheckAccessDeleted.cc
+++ b/zypp/misc/CheckAccessDeleted.cc
@@ -10,7 +10,12 @@
*
*/
#include <iostream>
-//#include "zypp/base/LogTools.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Exception.h"
+
+#include "zypp/PathInfo.h"
+#include "zypp/ExternalProgram.h"
#include "zypp/misc/CheckAccessDeleted.h"
@@ -22,14 +27,187 @@ using std::endl;
///////////////////////////////////////////////////////////////////
namespace zypp
{ /////////////////////////////////////////////////////////////////
+
///////////////////////////////////////////////////////////////////
- namespace misc
+ namespace
{ /////////////////////////////////////////////////////////////////
+ //
+ // lsof output lines are a sequence of NUL terminated fields,
+ // where the 1st char determines the fiels type.
+ //
+ // (pcuL) pid command userid loginname
+ // (ftkn).filedescriptor type linkcount filename
+ //
+ /////////////////////////////////////////////////////////////////
+ /** Add \c cache to \c data if the process is accessing deleted files.
+ * \c pid string in \c cache is the proc line \c (pcuLR), \c iles
+ * are lready in place. Always clear the \c cache.files!
+ */
+ inline void addDataIf( std::vectorCheckAccessDeleted::ProcInfo & data_r, CheckAccessDeleted::ProcInfo & cache_r )
+ {
+ if ( cache_r.files.empty() )
+ return;
+
+ // at least one file access so keep it:
+ data_r.push_back( CheckAccessDeleted::ProcInfo() );
+ CheckAccessDeleted::ProcInfo & pinfo( data_r.back() );
+
+ std::string pline;
+ cache_r.pid.swap( pline );
+ cache_r.files.swap( pinfo.files ); // clears cache.files
+
+ for_( ch, pline.begin(), pline.end() )
+ {
+ switch ( *ch )
+ {
+ case 'p':
+ pinfo.pid = &*(ch+1);
+ break;
+ case 'R':
+ pinfo.ppid = &*(ch+1);
+ break;
+ case 'u':
+ pinfo.puid = &*(ch+1);
+ break;
+ case 'L':
+ pinfo.login = &*(ch+1);
+ break;
+ case 'c':
+ pinfo.command = &*(ch+1);
+ break;
+ }
+ if ( *ch == '\n' ) break; // end of data
+ do { ++ch; } while ( *ch != '\0' ); // skip to next field
+ }
+ if ( pinfo.command.size() == 15 )
+ {
+ // the command name might be truncated, so we check against /proc/<pid>/exe
+ pinfo.command = filesystem::readlink( Pathname("/proc")/pinfo.pid/"exe" ).basename();
+ }
+ MIL << " Take " << pinfo << endl;
+ }
+
+ /** Add line to cache if it refers to a deleted file.
+ * Either the link count \c(k) os \c 0, or if no link cout is present,
+ * the filedescriptor/type \c (ft) is set to \c DEL.
+ */
+ inline void addCacheIf( CheckAccessDeleted::ProcInfo & cache_r, const std::string & line_r )
+ {
+ bool takeLine = false;
+ const char * name = "";
+
+ for_( ch, line_r.begin(), line_r.end() )
+ {
+ switch ( *ch )
+ {
+ case 'k':
+ if ( ! takeLine && *(ch+1) == '0' && *(ch+2) == '\0' )
+ takeLine = true;
+ break;
+ case 'f':
+ case 't':
+ if ( ! takeLine && *(ch+1) == 'D' && *(ch+2) == 'E' && *(ch+3) == 'L' && *(ch+4) == '\0' )
+ takeLine = true;
+ break;
+ case 'n':
+ name = &*(ch+1);
+ break;
+ }
+ if ( *ch == '\n' ) break; // end of data
+ do { ++ch; } while ( *ch != '\0' ); // skip to next field
+ }
+ if ( takeLine )
+ cache_r.files.push_back( name );
+ }
/////////////////////////////////////////////////////////////////
- } // namespace misc
+ } // namespace
///////////////////////////////////////////////////////////////////
- /////////////////////////////////////////////////////////////////
+
+ CheckAccessDeleted::size_type CheckAccessDeleted::check()
+ {
+ _data.clear();
+ std::vector<ProcInfo> data;
+
+ static const char* argv[] =
+ {
+ "lsof", "-n", "-FpcuLRftkn0", NULL
+ };
+ ExternalProgram prog( argv, ExternalProgram::Discard_Stderr );
+
+ CheckAccessDeleted::ProcInfo cache;
+ for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
+ {
+ if ( line[0] == 'p' )
+ {
+ addDataIf( data, cache );
+ cache.pid = line; //
+ }
+ else
+ {
+ addCacheIf( cache, line );
+ }
+ }
+ addDataIf( data, cache );
+
+ int ret = prog.close();
+ if ( ret != 0 )
+ {
+ Exception err( str::form("Executing 'lsof' failed (%d).", ret) );
+ err.remember( prog.execError() );
+ ZYPP_THROW( err );
+ }
+
+ _data.swap( data );
+ return _data.size();
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ namespace
+ { /////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////
+ } // namespace
+ ///////////////////////////////////////////////////////////////////
+
+ std::string CheckAccessDeleted::ProcInfo::service() const
+ {
+ // TODO: This needs to be implemented.
+ return std::string();
+ }
+
+ /******************************************************************
+ **
+ ** FUNCTION NAME : operator<<
+ ** FUNCTION TYPE : std::ostream &
+ */
+ std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted & obj )
+ {
+ return dumpRange( str << "CheckAccessDeleted ",
+ obj.begin(),
+ obj.end() );
+ }
+
+ /******************************************************************
+ **
+ ** FUNCTION NAME : operator<<
+ ** FUNCTION TYPE : std::ostream &
+ */
+ std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted::ProcInfo & obj )
+ {
+ if ( obj.pid.empty() )
+ return str << "<NoProc>";
+
+ return dumpRangeLine( str << obj.command
+ << '<' << obj.pid
+ << '|' << obj.ppid
+ << '|' << obj.puid
+ << '|' << obj.login
+ << '>',
+ obj.files.begin(),
+ obj.files.end() );
+ }
+
+ /////////////////////////////////////////////////////////////////
} // namespace zypp
///////////////////////////////////////////////////////////////////
diff --git a/zypp/misc/CheckAccessDeleted.h b/zypp/misc/CheckAccessDeleted.h
index 75c7908..1e9513a 100644
--- a/zypp/misc/CheckAccessDeleted.h
+++ b/zypp/misc/CheckAccessDeleted.h
@@ -13,26 +13,83 @@
#define ZYPP_MISC_CHECKACCESSDELETED_H
#include <iosfwd>
-
-#include "zypp/base/PtrTypes.h"
+#include <vector>
+#include <string>
///////////////////////////////////////////////////////////////////
namespace zypp
{ /////////////////////////////////////////////////////////////////
+
+ /**
+ * Check for running processes which access deleted files or libraries.
+ *
+ * Executed after commit, this gives a hint which processes/services
+ * need to be restarted.
+ *
+ * Per default upon construtcion or explicit call to \ref check,
+ * information about running processes which access deleted files
+ * or libraries is collected and provided as a \ref ProcInfo
+ * container.
+ */
+ class CheckAccessDeleted
+ {
+ public:
+ /**
+ * Data about one running process accessing deleted files.
+ */
+ struct ProcInfo
+ {
+ std::string pid; //!< process ID
+ std::string ppid; //!< parent process ID
+ std::string puid; //!< process user ID
+ std::string login; //!< process login name
+ std::string command; //!< process command name
+ std::vectorstd::string files; //!< list of deleted files or libraries accessed
+
+ /** Guess if command was started by an \c /etc/init.d/ script.
+ * The name of an \c /etc/init.d/ script that might be used to restart the
+ * command.
+ * \warning This is just a guess.
+ */
+ std::string service() const;
+ };
+
+ typedef size_t size_type;
+ typedef ProcInfo value_type;
+ typedef std::vector<ProcInfo>::const_iterator const_iterator;
+
+ public:
+ /** Default ctor performs check immediately.
+ * Pass \c false and the initial check is omitted.
+ * \throws Exception if \ref check throws.
+ * \see \ref check.
+ */
+ CheckAccessDeleted( bool doCheck_r = true )
+ { if ( doCheck_r ) check(); }
+
+ public:
+ /** Check for running processes which access deleted files or libraries.
+ * \return the number of processes found.
+ * \throws Exception On error collecting the data (e.g. no lsof installed)
+ */
+ size_type check();
+
+ bool empty() const { return _data.empty(); }
+ size_type size() const { return _data.size(); }
+ const_iterator begin() const { return _data.begin(); }
+ const_iterator end() const { return _data.end(); }
+
+ private:
+ std::vector<ProcInfo> _data;
+ };
///////////////////////////////////////////////////////////////////
- namespace misc
- { /////////////////////////////////////////////////////////////////
-
- /**
- * Check for running programms which access deleted files or libraries.
- *
- * Executed after commit, this gives a hint which programms/services
- * need to be restarted.
- */
-
- /////////////////////////////////////////////////////////////////
- } // namespace misc
- ///////////////////////////////////////////////////////////////////
+
+ /** \relates CheckAccessDeleted Stream output */
+ std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted & obj );
+
+ /** \relates CheckAccessDeleted::ProcInfo Stream output */
+ std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted::ProcInfo & obj );
+
/////////////////////////////////////////////////////////////////
} // namespace zypp
///////////////////////////////////////////////////////////////////
--
To unsubscribe, e-mail: zypp-commit+unsubscribe@opensuse.org
For additional commands, e-mail: zypp-commit+help@opensuse.org