ZYppFactory.cc
Go to the documentation of this file.00001
00002
00003
00004
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 }
00070
00076 class ZYppGlobalLock
00077 {
00078 public:
00079 ZYppGlobalLock()
00080 : _cleanLock( false )
00081 , _zyppLockFilePath( env::ZYPP_LOCKFILE_ROOT() / "/var/run/zypp.pid" )
00082 , _zyppLockFile( NULL )
00083 , _lockerPid( 0 )
00084 {
00085 filesystem::assert_dir(_zyppLockFilePath.dirname() );
00086 }
00087
00088 ~ZYppGlobalLock()
00089 {
00090 if ( _cleanLock )
00091 try {
00092
00093 ScopedGuard closeOnReturn( accessLockFile() );
00094 {
00095 scoped_lock<file_lock> flock( *_zyppLockFileLockPtr );
00096
00097
00098 ftruncate( fileno(_zyppLockFile), 0 );
00099 }
00100 MIL << "Cleanned lock file. (" << getpid() << ")" << std::endl;
00101 }
00102 catch(...) {}
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;
00145
00146
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;
00159
00160 clearerr( _zyppLockFile );
00161 fflush( _zyppLockFile );
00162
00163
00164
00165
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
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
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;
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;
00230
00231
00232 ScopedGuard closeOnReturn( accessLockFile() );
00233 {
00234 scoped_lock<file_lock> flock( *_zyppLockFileLockPtr );
00235
00236 _lockerPid = readLockFile();
00237 if ( _lockerPid == 0 )
00238 {
00239
00240 writeLockFile();
00241 return false;
00242 }
00243 else if ( _lockerPid == getpid() )
00244 {
00245
00246 return false;
00247 }
00248 else
00249 {
00250
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
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
00298
00300
00302
00303
00304
00305
00306 ZYppFactory ZYppFactory::instance()
00307 {
00308 return ZYppFactory();
00309 }
00310
00312
00313
00314
00315
00316 ZYppFactory::ZYppFactory()
00317 {
00318
00319 }
00320
00322
00323
00324
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() )
00357 sleep( delay );
00358 else
00359 {
00360 MIL << "Retry after " << i << " sec." << endl;
00361 failed = globalLock().zyppLocked();
00362 if ( failed )
00363 {
00364
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;
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
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
00404
00405
00406 std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
00407 {
00408 return str << "ZYppFactory";
00409 }
00410
00412 }