libzypp 17.25.7
ZYppFactory.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
12extern "C"
13{
14#include <sys/file.h>
15}
16#include <iostream>
17#include <fstream>
18#include <signal.h>
19
20#include <zypp/base/Logger.h>
21#include <zypp/base/Gettext.h>
22#include <zypp/base/IOStream.h>
24#include <zypp/base/Backtrace.h>
25#include <zypp/PathInfo.h>
26
27#include <zypp/ZYppFactory.h>
29
30#include <boost/interprocess/sync/file_lock.hpp>
31#include <boost/interprocess/sync/scoped_lock.hpp>
32#include <boost/interprocess/sync/sharable_lock.hpp>
33
34using boost::interprocess::file_lock;
35using boost::interprocess::scoped_lock;
36using boost::interprocess::sharable_lock;
37
38using std::endl;
39
40namespace zyppintern { void repoVariablesReset(); } // upon re-acquiring the lock...
41
43namespace zypp
44{
45
46 namespace
47 {
48 void sigsegvHandler( int sig );
49 ::sighandler_t lastSigsegvHandler = ::signal( SIGSEGV, sigsegvHandler );
50
52 void sigsegvHandler( int sig )
53 {
54 INT << "Error: signal " << sig << endl << dumpBacktrace << endl;
55 ::signal( SIGSEGV, lastSigsegvHandler );
56 }
57 }
58
59 namespace env
60 {
63 { return getenv("ZYPP_LOCKFILE_ROOT") ? getenv("ZYPP_LOCKFILE_ROOT") : "/"; }
64 }
65
67 namespace zypp_readonly_hack
68 {
69
70 static bool active = getenv("ZYPP_READONLY_HACK");
71
72 void IWantIt() // see zypp/zypp_detail/ZYppReadOnlyHack.h
73 {
74 active = true;
75 MIL << "ZYPP_READONLY promised." << endl;
76 }
77
78 bool IGotIt()
79 {
80 return active;
81 }
82
84 } // namespace zypp_readonly_hack
86
93 {
94 public:
96 : _zyppLockFilePath( env::ZYPP_LOCKFILE_ROOT() / "/run/zypp.pid" )
97 , _zyppLockFile( NULL )
98 , _lockerPid( 0 )
99 , _cleanLock( false )
100 {
102 }
103
105 {
106 if ( _cleanLock )
107 try {
108 // Exception safe access to the lockfile.
109 ScopedGuard closeOnReturn( accessLockFile() );
110 {
111 scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
112 // Truncate the file rather than deleting it. Other processes may
113 // still use it to synchronsize.
114 ftruncate( fileno(_zyppLockFile), 0 );
115 }
116 MIL << "Cleaned lock file. (" << getpid() << ")" << std::endl;
117 }
118 catch(...) {} // let no exception escape.
119 }
120
121 pid_t lockerPid() const
122 { return _lockerPid; }
123
124 const std::string & lockerName() const
125 { return _lockerName; }
126
128 { return _zyppLockFilePath; }
129
130
131 private:
135
137 std::string _lockerName;
139
140 private:
141 typedef shared_ptr<void> ScopedGuard;
142
150 {
152 return ScopedGuard( static_cast<void*>(0),
153 bind( mem_fun_ref( &ZYppGlobalLock::_closeLockFile ), ref(*this) ) );
154 }
155
158 {
159 if ( _zyppLockFile != NULL )
160 return; // is open
161
162 // open pid file rw so we are sure it exist when creating the flock
163 _zyppLockFile = fopen( _zyppLockFilePath.c_str(), "a+" );
164 if ( _zyppLockFile == NULL )
165 ZYPP_THROW( Exception( "Cant open " + _zyppLockFilePath.asString() ) );
167 MIL << "Open lockfile " << _zyppLockFilePath << endl;
168 }
169
172 {
173 if ( _zyppLockFile == NULL )
174 return; // is closed
175
176 clearerr( _zyppLockFile );
177 fflush( _zyppLockFile );
178 // http://www.boost.org/doc/libs/1_50_0/doc/html/interprocess/synchronization_mechanisms.html
179 // If you are using a std::fstream/native file handle to write to the file
180 // while using file locks on that file, don't close the file before releasing
181 // all the locks of the file.
182 _zyppLockFileLock = file_lock();
183 fclose( _zyppLockFile );
184 _zyppLockFile = NULL;
185 MIL << "Close lockfile " << _zyppLockFilePath << endl;
186 }
187
188
189 bool isProcessRunning( pid_t pid_r )
190 {
191 // it is another program, not me, see if it is still running
192 Pathname procdir( Pathname("/proc")/str::numstring(pid_r) );
193 PathInfo status( procdir );
194 MIL << "Checking " << status << endl;
195
196 if ( ! status.isDir() )
197 {
198 DBG << "No such process." << endl;
199 return false;
200 }
201
202 static char buffer[513];
203 buffer[0] = buffer[512] = 0;
204 // man proc(5): /proc/[pid]/cmdline is empty if zombie.
205 if ( std::ifstream( (procdir/"cmdline").c_str() ).read( buffer, 512 ).gcount() > 0 )
206 {
207 _lockerName = buffer;
208 DBG << "Is running: " << _lockerName << endl;
209 return true;
210 }
211
212 DBG << "In zombie state." << endl;
213 return false;
214 }
215
217 {
218 clearerr( _zyppLockFile );
219 fseek( _zyppLockFile, 0, SEEK_SET );
220 long readpid = 0;
221 fscanf( _zyppLockFile, "%ld", &readpid );
222 MIL << "read: Lockfile " << _zyppLockFilePath << " has pid " << readpid << " (our pid: " << getpid() << ") "<< std::endl;
223 return (pid_t)readpid;
224 }
225
227 {
228 clearerr( _zyppLockFile );
229 fseek( _zyppLockFile, 0, SEEK_SET );
230 ftruncate( fileno(_zyppLockFile), 0 );
231 fprintf(_zyppLockFile, "%ld\n", (long)getpid() );
232 fflush( _zyppLockFile );
233 _cleanLock = true; // cleanup on exit
234 MIL << "write: Lockfile " << _zyppLockFilePath << " got pid " << getpid() << std::endl;
235 }
236
237 public:
238
243 {
244 if ( geteuid() != 0 )
245 return false; // no lock as non-root
246
247 // Exception safe access to the lockfile.
248 ScopedGuard closeOnReturn( accessLockFile() );
249 {
250 scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
251
253 if ( _lockerPid == 0 )
254 {
255 // no or empty lock file
257 return false;
258 }
259 else if ( _lockerPid == getpid() )
260 {
261 // keep my own lock
262 return false;
263 }
264 else
265 {
266 // a foreign pid in lock
268 {
269 WAR << _lockerPid << " is running and has a ZYpp lock. Sorry." << std::endl;
270 return true;
271 }
272 else
273 {
274 MIL << _lockerPid << " is dead. Taking the lock file." << std::endl;
276 return false;
277 }
278 }
279 }
280 INT << "Oops! We should not be here!" << std::endl;
281 return true;
282 }
283
284 };
285
287 namespace
288 {
289 static weak_ptr<ZYpp> _theZYppInstance;
290 static scoped_ptr<ZYppGlobalLock> _theGlobalLock; // on/off in sync with _theZYppInstance
291
292 ZYppGlobalLock & globalLock()
293 {
294 if ( !_theGlobalLock )
295 _theGlobalLock.reset( new ZYppGlobalLock );
296 return *_theGlobalLock;
297 }
298 } //namespace
300
302 //
303 // CLASS NAME : ZYpp
304 //
306
307 ZYpp::ZYpp( const Impl_Ptr & impl_r )
308 : _pimpl( impl_r )
309 {
310 ::zyppintern::repoVariablesReset(); // upon re-acquiring the lock...
311 MIL << "ZYpp is on..." << endl;
312 }
313
315 {
316 _theGlobalLock.reset();
317 MIL << "ZYpp is off..." << endl;
318 }
319
321 //
322 // CLASS NAME : ZYppFactoryException
323 //
325
326 ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r )
327 : Exception( msg_r )
328 , _lockerPid( lockerPid_r )
329 , _lockerName( lockerName_r )
330 {}
331
333 {}
334
336 //
337 // CLASS NAME : ZYppFactory
338 //
340
342 { return ZYppFactory(); }
343
345 {}
346
348 {}
349
351 //
353 {
354 ZYpp::Ptr _instance = _theZYppInstance.lock();
355 if ( ! _instance )
356 {
357 if ( geteuid() != 0 )
358 {
359 MIL << "Running as user. Skip creating " << globalLock().zyppLockFilePath() << std::endl;
360 }
362 {
363 MIL << "ZYPP_READONLY active." << endl;
364 }
365 else if ( globalLock().zyppLocked() )
366 {
367 bool failed = true;
368 const long LOCK_TIMEOUT = str::strtonum<long>( getenv( "ZYPP_LOCK_TIMEOUT" ) );
369 if ( LOCK_TIMEOUT > 0 )
370 {
371 MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within $LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec." << endl;
372 unsigned delay = 1;
373 Pathname procdir( Pathname("/proc")/str::numstring(globalLock().lockerPid()) );
374 for ( long i = 0; i < LOCK_TIMEOUT; i += delay )
375 {
376 if ( PathInfo( procdir ).isDir() ) // wait for /proc/pid to disapear
377 sleep( delay );
378 else
379 {
380 MIL << "Retry after " << i << " sec." << endl;
381 failed = globalLock().zyppLocked();
382 if ( failed )
383 {
384 // another proc locked faster. maybe it ends fast as well....
385 MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within " << (LOCK_TIMEOUT-i) << " sec." << endl;
386 procdir = Pathname( Pathname("/proc")/str::numstring(globalLock().lockerPid()) );
387 }
388 else
389 {
390 MIL << "Finally got the lock!" << endl;
391 break; // gotcha
392 }
393 }
394 }
395 }
396 if ( failed )
397 {
398 std::string t = str::form(_("System management is locked by the application with pid %d (%s).\n"
399 "Close this application before trying again."),
400 globalLock().lockerPid(),
401 globalLock().lockerName().c_str()
402 );
403 ZYPP_THROW(ZYppFactoryException(t, globalLock().lockerPid(), globalLock().lockerName() ));
404 }
405 }
406 // Here we go...
407 static ZYpp::Impl_Ptr _theImplInstance; // for now created once
408 if ( !_theImplInstance )
409 _theImplInstance.reset( new ZYpp::Impl );
410 _instance.reset( new ZYpp( _theImplInstance ) );
411 _theZYppInstance = _instance;
412 }
413
414 return _instance;
415 }
416
418 //
420 { return !_theZYppInstance.expired(); }
421
422 /******************************************************************
423 **
424 ** FUNCTION NAME : operator<<
425 ** FUNCTION TYPE : std::ostream &
426 */
427 std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
428 {
429 return str << "ZYppFactory";
430 }
431
433} // namespace zypp
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:392
Interface to gettext.
#define _(MSG)
Definition: Gettext.h:37
#define DBG
Definition: Logger.h:78
#define MIL
Definition: Logger.h:79
#define WAR
Definition: Logger.h:80
#define INT
Definition: Logger.h:83
Base class for Exception.
Definition: Exception.h:146
ZYppFactoryException(const std::string &msg_r, pid_t lockerPid_r, const std::string &lockerName_r)
Definition: ZYppFactory.cc:326
ZYpp factory class (Singleton)
Definition: ZYppFactory.h:44
static ZYppFactory instance()
Singleton ctor.
Definition: ZYppFactory.cc:341
bool haveZYpp() const
Whether the ZYpp instance is already created.
Definition: ZYppFactory.cc:419
ZYpp::Ptr getZYpp() const
Definition: ZYppFactory.cc:352
ZYppFactory()
Default ctor.
Definition: ZYppFactory.cc:344
Our broken global lock.
Definition: ZYppFactory.cc:93
const std::string & lockerName() const
Definition: ZYppFactory.cc:124
void _closeLockFile()
Use accessLockFile.
Definition: ZYppFactory.cc:171
std::string _lockerName
Definition: ZYppFactory.cc:137
file_lock _zyppLockFileLock
Definition: ZYppFactory.cc:133
void _openLockFile()
Use accessLockFile.
Definition: ZYppFactory.cc:157
shared_ptr< void > ScopedGuard
Definition: ZYppFactory.cc:141
pid_t lockerPid() const
Definition: ZYppFactory.cc:121
bool zyppLocked()
Try to aquire a lock.
Definition: ZYppFactory.cc:242
ScopedGuard accessLockFile()
Exception safe access to the lockfile.
Definition: ZYppFactory.cc:149
Pathname _zyppLockFilePath
Definition: ZYppFactory.cc:132
const Pathname & zyppLockFilePath() const
Definition: ZYppFactory.cc:127
bool isProcessRunning(pid_t pid_r)
Definition: ZYppFactory.cc:189
shared_ptr< Impl > Impl_Ptr
Definition: ZYpp.h:147
~ZYpp()
Dtor.
Definition: ZYppFactory.cc:314
ZYpp(const Impl_Ptr &impl_r)
Factory ctor.
Definition: ZYppFactory.cc:307
::boost::shared_ptr< ZYpp > Ptr
Definition: ZYpp.h:60
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:221
Pathname dirname() const
Return all but the last component od this path.
Definition: Pathname.h:124
const char * c_str() const
String representation.
Definition: Pathname.h:110
const std::string & asString() const
String representation.
Definition: Pathname.h:91
String related utilities and Regular expression matching.
std::map< std::string, std::string > read(const Pathname &_path)
Read sysconfig file path_r and return (key,valye) pairs.
Definition: Sysconfig.cc:34
Pathname ZYPP_LOCKFILE_ROOT()
Hack to circumvent the currently poor –root support.
Definition: ZYppFactory.cc:62
int assert_dir(const Pathname &path, unsigned mode)
Like 'mkdir -p'.
Definition: PathInfo.cc:320
std::string numstring(char n, int w=0)
Definition: String.h:286
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:36
void IWantIt() ZYPP_DEPRECATED
Definition: ZYppFactory.cc:72
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:2
std::ostream & operator<<(std::ostream &str, const Exception &obj)
Definition: Exception.cc:147
std::ostream & dumpBacktrace(std::ostream &stream_r)
Dump current stack trace to a stream.
Definition: Backtrace.cc:24
void repoVariablesReset()