libzypp 9.41.1
|
00001 /*---------------------------------------------------------------------\ 00002 | ____ _ __ __ ___ | 00003 | |__ / \ / / . \ . \ | 00004 | / / \ V /| _/ _/ | 00005 | / /__ | | | | | | | 00006 | /_____||_| |_| |_| | 00007 | | 00008 \---------------------------------------------------------------------*/ 00012 extern "C" 00013 { 00014 #include <sys/file.h> 00015 } 00016 #include <iostream> 00017 #include <fstream> 00018 00019 #include "zypp/base/Logger.h" 00020 #include "zypp/base/Gettext.h" 00021 #include "zypp/base/IOStream.h" 00022 #include "zypp/base/Functional.h" 00023 #include "zypp/PathInfo.h" 00024 00025 #include "zypp/ZYppFactory.h" 00026 #include "zypp/zypp_detail/ZYppImpl.h" 00027 #include "zypp/zypp_detail/ZYppReadOnlyHack.h" 00028 00029 #include <boost/interprocess/sync/file_lock.hpp> 00030 #include <boost/interprocess/sync/scoped_lock.hpp> 00031 #include <boost/interprocess/sync/sharable_lock.hpp> 00032 00033 using boost::interprocess::file_lock; 00034 using boost::interprocess::scoped_lock; 00035 using boost::interprocess::sharable_lock; 00036 00037 using std::endl; 00038 00040 namespace zypp 00041 { 00042 00043 namespace env 00044 { 00046 inline Pathname ZYPP_LOCKFILE_ROOT() 00047 { return getenv("ZYPP_LOCKFILE_ROOT") ? getenv("ZYPP_LOCKFILE_ROOT") : "/"; } 00048 } 00049 00051 namespace zypp_readonly_hack 00052 { 00053 00054 static bool active = false; 00055 00056 void IWantIt() 00057 { 00058 active = true; 00059 MIL << "ZYPP_READONLY promised." << endl; 00060 } 00061 00062 bool IGotIt() 00063 { 00064 return active; 00065 } 00066 00068 } // namespace zypp_readonly_hack 00070 00076 class ZYppGlobalLock 00077 { 00078 public: 00079 ZYppGlobalLock() 00080 : _zyppLockFilePath( env::ZYPP_LOCKFILE_ROOT() / "/var/run/zypp.pid" ) 00081 , _zyppLockFile( NULL ) 00082 , _lockerPid( 0 ) 00083 , _cleanLock( false ) 00084 { 00085 filesystem::assert_dir(_zyppLockFilePath.dirname() ); 00086 } 00087 00088 ~ZYppGlobalLock() 00089 { 00090 if ( _cleanLock ) 00091 try { 00092 // Exception safe access to the lockfile. 00093 ScopedGuard closeOnReturn( accessLockFile() ); 00094 { 00095 scoped_lock<file_lock> flock( *_zyppLockFileLockPtr ); // aquire write lock 00096 // Truncate the file rather than deleting it. Other processes may 00097 // still use it to synchronsize. 00098 ftruncate( fileno(_zyppLockFile), 0 ); 00099 } 00100 MIL << "Cleanned lock file. (" << getpid() << ")" << std::endl; 00101 } 00102 catch(...) {} // let no exception escape. 00103 } 00104 00105 pid_t lockerPid() const 00106 { return _lockerPid; } 00107 00108 const std::string & lockerName() const 00109 { return _lockerName; } 00110 00111 const Pathname & zyppLockFilePath() const 00112 { return _zyppLockFilePath; } 00113 00114 00115 private: 00116 Pathname _zyppLockFilePath; 00117 scoped_ptr<file_lock> _zyppLockFileLockPtr; 00118 FILE * _zyppLockFile; 00119 00120 pid_t _lockerPid; 00121 std::string _lockerName; 00122 bool _cleanLock; 00123 00124 private: 00125 typedef shared_ptr<void> ScopedGuard; 00126 00133 ScopedGuard accessLockFile() 00134 { 00135 _openLockFile(); 00136 return ScopedGuard( static_cast<void*>(0), 00137 bind( mem_fun_ref( &ZYppGlobalLock::_closeLockFile ), ref(*this) ) ); 00138 } 00139 00141 void _openLockFile() 00142 { 00143 if ( _zyppLockFile != NULL ) 00144 return; // is open 00145 00146 // open pid file rw so we are sure it exist when creating the flock 00147 _zyppLockFile = fopen( _zyppLockFilePath.c_str(), "a+" ); 00148 if ( _zyppLockFile == NULL ) 00149 ZYPP_THROW( Exception( "Cant open " + _zyppLockFilePath.asString() ) ); 00150 _zyppLockFileLockPtr.reset( new file_lock( _zyppLockFilePath.c_str() ) ); 00151 MIL << "Open lockfile " << _zyppLockFilePath << endl; 00152 } 00153 00155 void _closeLockFile() 00156 { 00157 if ( _zyppLockFile == NULL ) 00158 return; // is closed 00159 00160 clearerr( _zyppLockFile ); 00161 fflush( _zyppLockFile ); 00162 // http://www.boost.org/doc/libs/1_50_0/doc/html/interprocess/synchronization_mechanisms.html 00163 // If you are using a std::fstream/native file handle to write to the file 00164 // while using file locks on that file, don't close the file before releasing 00165 // all the locks of the file. 00166 _zyppLockFileLockPtr.reset(); 00167 fclose( _zyppLockFile ); 00168 _zyppLockFile = NULL; 00169 MIL << "Close lockfile " << _zyppLockFilePath << endl; 00170 } 00171 00172 00173 bool isProcessRunning( pid_t pid_r ) 00174 { 00175 // it is another program, not me, see if it is still running 00176 Pathname procdir( "/proc"/str::numstring(pid_r) ); 00177 PathInfo status( procdir ); 00178 MIL << "Checking " << status << endl; 00179 00180 if ( ! status.isDir() ) 00181 { 00182 DBG << "No such process." << endl; 00183 return false; 00184 } 00185 00186 static char buffer[513]; 00187 buffer[0] = buffer[512] = 0; 00188 // man proc(5): /proc/[pid]/cmdline is empty if zombie. 00189 if ( std::ifstream( (procdir/"cmdline").c_str() ).read( buffer, 512 ).gcount() > 0 ) 00190 { 00191 _lockerName = buffer; 00192 DBG << "Is running: " << _lockerName << endl; 00193 return true; 00194 } 00195 00196 DBG << "In zombie state." << endl; 00197 return false; 00198 } 00199 00200 pid_t readLockFile() 00201 { 00202 clearerr( _zyppLockFile ); 00203 fseek( _zyppLockFile, 0, SEEK_SET ); 00204 long readpid = 0; 00205 fscanf( _zyppLockFile, "%ld", &readpid ); 00206 MIL << "read: Lockfile " << _zyppLockFilePath << " has pid " << readpid << " (our pid: " << getpid() << ") "<< std::endl; 00207 return (pid_t)readpid; 00208 } 00209 00210 void writeLockFile() 00211 { 00212 clearerr( _zyppLockFile ); 00213 fseek( _zyppLockFile, 0, SEEK_SET ); 00214 ftruncate( fileno(_zyppLockFile), 0 ); 00215 fprintf(_zyppLockFile, "%ld\n", (long)getpid() ); 00216 fflush( _zyppLockFile ); 00217 _cleanLock = true; // cleanup on exit 00218 MIL << "write: Lockfile " << _zyppLockFilePath << " got pid " << getpid() << std::endl; 00219 } 00220 00221 public: 00222 00226 bool zyppLocked() 00227 { 00228 if ( geteuid() != 0 ) 00229 return false; // no lock as non-root 00230 00231 // Exception safe access to the lockfile. 00232 ScopedGuard closeOnReturn( accessLockFile() ); 00233 { 00234 scoped_lock<file_lock> flock( *_zyppLockFileLockPtr ); // aquire write lock 00235 00236 _lockerPid = readLockFile(); 00237 if ( _lockerPid == 0 ) 00238 { 00239 // no or empty lock file 00240 writeLockFile(); 00241 return false; 00242 } 00243 else if ( _lockerPid == getpid() ) 00244 { 00245 // keep my own lock 00246 return false; 00247 } 00248 else 00249 { 00250 // a foreign pid in lock 00251 if ( isProcessRunning( _lockerPid ) ) 00252 { 00253 WAR << _lockerPid << " is running and has a ZYpp lock. Sorry." << std::endl; 00254 return true; 00255 } 00256 else 00257 { 00258 MIL << _lockerPid << " is dead. Taking the lock file." << std::endl; 00259 writeLockFile(); 00260 return false; 00261 } 00262 } 00263 } 00264 INT << "Oops! We should not be here!" << std::endl; 00265 return true; 00266 } 00267 00268 }; 00269 00270 namespace 00271 { 00272 static ZYppGlobalLock & globalLock() 00273 { 00274 static ZYppGlobalLock lock; 00275 return lock; 00276 } 00277 bool _haveZYpp = false; 00278 } 00279 00281 // 00282 // CLASS NAME : ZYppFactoryException 00283 // 00285 00286 ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r ) 00287 : Exception( msg_r ) 00288 , _lockerPid( lockerPid_r ) 00289 , _lockerName( lockerName_r ) 00290 {} 00291 00292 ZYppFactoryException::~ZYppFactoryException() throw () 00293 {} 00294 00296 // 00297 // CLASS NAME : ZYppFactory 00298 // 00300 00302 // 00303 // METHOD NAME : ZYppFactory::instance 00304 // METHOD TYPE : ZYppFactory 00305 // 00306 ZYppFactory ZYppFactory::instance() 00307 { 00308 return ZYppFactory(); 00309 } 00310 00312 // 00313 // METHOD NAME : ZYppFactory::ZYppFactory 00314 // METHOD TYPE : Ctor 00315 // 00316 ZYppFactory::ZYppFactory() 00317 { 00318 00319 } 00320 00322 // 00323 // METHOD NAME : ZYppFactory::~ZYppFactory 00324 // METHOD TYPE : Dtor 00325 // 00326 ZYppFactory::~ZYppFactory() 00327 {} 00328 00330 // 00331 ZYpp::Ptr ZYppFactory::getZYpp() const 00332 { 00333 static ZYpp::Ptr _instance; 00334 00335 if ( ! _instance ) 00336 { 00337 if ( geteuid() != 0 ) 00338 { 00339 MIL << "Running as user. Skip creating " << globalLock().zyppLockFilePath() << std::endl; 00340 } 00341 else if ( zypp_readonly_hack::active ) 00342 { 00343 MIL << "ZYPP_READONLY active." << endl; 00344 } 00345 else if ( globalLock().zyppLocked() ) 00346 { 00347 bool failed = true; 00348 const long LOCK_TIMEOUT = str::strtonum<long>( getenv( "ZYPP_LOCK_TIMEOUT" ) ); 00349 if ( LOCK_TIMEOUT > 0 ) 00350 { 00351 MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within $LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec." << endl; 00352 unsigned delay = 1; 00353 Pathname procdir( "/proc"/str::numstring(globalLock().lockerPid()) ); 00354 for ( long i = 0; i < LOCK_TIMEOUT; i += delay ) 00355 { 00356 if ( PathInfo( procdir ).isDir() ) // wait for /proc/pid to disapear 00357 sleep( delay ); 00358 else 00359 { 00360 MIL << "Retry after " << i << " sec." << endl; 00361 failed = globalLock().zyppLocked(); 00362 if ( failed ) 00363 { 00364 // another proc locked faster. maybe it ends fast as well.... 00365 MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within " << (LOCK_TIMEOUT-i) << " sec." << endl; 00366 procdir = Pathname( "/proc"/str::numstring(globalLock().lockerPid()) ); 00367 } 00368 else 00369 { 00370 MIL << "Finally got the lock!" << endl; 00371 break; // gotcha 00372 } 00373 } 00374 } 00375 00376 } 00377 if ( failed ) 00378 { 00379 std::string t = str::form(_("System management is locked by the application with pid %d (%s).\n" 00380 "Close this application before trying again."), 00381 globalLock().lockerPid(), 00382 globalLock().lockerName().c_str() 00383 ); 00384 ZYPP_THROW(ZYppFactoryException(t, globalLock().lockerPid(), globalLock().lockerName() )); 00385 } 00386 } 00387 // Here we go... 00388 _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) ); 00389 if ( _instance ) 00390 _haveZYpp = true; 00391 } 00392 00393 return _instance; 00394 } 00395 00397 // 00398 bool ZYppFactory::haveZYpp() const 00399 { return _haveZYpp; } 00400 00401 /****************************************************************** 00402 ** 00403 ** FUNCTION NAME : operator<< 00404 ** FUNCTION TYPE : std::ostream & 00405 */ 00406 std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj ) 00407 { 00408 return str << "ZYppFactory"; 00409 } 00410 00412 } // namespace zypp