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