libzypp 17.35.1
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>
22#include <zypp/base/Gettext.h>
23#include <zypp/base/IOStream.h>
25#include <zypp/base/Backtrace.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#include <utility>
36
37#include <iostream>
38
39using boost::interprocess::file_lock;
40using boost::interprocess::scoped_lock;
41using boost::interprocess::sharable_lock;
42
43using std::endl;
44
45namespace zyppintern { void repoVariablesReset(); } // upon re-acquiring the lock...
46
48namespace zypp
49{
50
51 namespace sighandler
52 {
54 template <int SIG>
56 {
57 static void backtraceHandler( int sig ) {
58 INT << "Error: signal " << SIG << endl << dumpBacktrace << endl;
60 ::signal( SIG, lastSigHandler );
61 }
62 static ::sighandler_t lastSigHandler;
63 };
64 template <int SIG>
66
67 // Explicit instantiation installs the handler:
68 template class SigBacktraceHandler<SIGSEGV>;
69 template class SigBacktraceHandler<SIGABRT>;
70 }
71
72 namespace env
73 {
76 { return getenv("ZYPP_LOCKFILE_ROOT") ? getenv("ZYPP_LOCKFILE_ROOT") : "/"; }
77 }
78
80 namespace zypp_readonly_hack
81 {
82
83 static bool active = getenv("ZYPP_READONLY_HACK");
84
85 ZYPP_API void IWantIt() // see zypp/zypp_detail/ZYppReadOnlyHack.h
86 {
87 active = true;
88 MIL << "ZYPP_READONLY promised." << endl;
89 }
90
91 bool IGotIt()
92 {
93 return active;
94 }
95
97 } // namespace zypp_readonly_hack
99
106 {
107 public:
109 : _zyppLockFilePath(std::move(lFilePath)), _zyppLockFile(NULL),
110 _lockerPid(0), _cleanLock(false) {
111 filesystem::assert_dir(_zyppLockFilePath.dirname() );
112 }
113
118
120 {
121 if ( _cleanLock )
122 try {
123 // Exception safe access to the lockfile.
124 ScopedGuard closeOnReturn( accessLockFile() );
125 {
126 scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
127 // Truncate the file rather than deleting it. Other processes may
128 // still use it to synchronsize.
129 ftruncate( fileno(_zyppLockFile), 0 );
130 }
131 MIL << "Cleaned lock file. (" << getpid() << ")" << std::endl;
132 }
133 catch(...) {} // let no exception escape.
134 }
135
136 pid_t lockerPid() const
137 { return _lockerPid; }
138
139 const std::string & lockerName() const
140 { return _lockerName; }
141
143 { return _zyppLockFilePath; }
144
145
146 private:
150
152 std::string _lockerName;
154
155 private:
156 using ScopedGuard = shared_ptr<void>;
157
165 {
166 _openLockFile();
167 return ScopedGuard( static_cast<void*>(0),
168 std::bind( std::mem_fn( &ZYppGlobalLock::_closeLockFile ), this ) );
169 }
170
173 {
174 if ( _zyppLockFile != NULL )
175 return; // is open
176
177 // open pid file rw so we are sure it exist when creating the flock
178 _zyppLockFile = fopen( _zyppLockFilePath.c_str(), "a+" );
179 if ( _zyppLockFile == NULL )
180 ZYPP_THROW( Exception( "Cant open " + _zyppLockFilePath.asString() ) );
181 _zyppLockFileLock = _zyppLockFilePath.c_str();
182 MIL << "Open lockfile " << _zyppLockFilePath << endl;
183 }
184
187 {
188 if ( _zyppLockFile == NULL )
189 return; // is closed
190
191 clearerr( _zyppLockFile );
192 fflush( _zyppLockFile );
193 // http://www.boost.org/doc/libs/1_50_0/doc/html/interprocess/synchronization_mechanisms.html
194 // If you are using a std::fstream/native file handle to write to the file
195 // while using file locks on that file, don't close the file before releasing
196 // all the locks of the file.
197 _zyppLockFileLock = file_lock();
198 fclose( _zyppLockFile );
199 _zyppLockFile = NULL;
200 MIL << "Close lockfile " << _zyppLockFilePath << endl;
201 }
202
203
204 bool isProcessRunning( pid_t pid_r )
205 {
206 // it is another program, not me, see if it is still running
207 Pathname procdir( Pathname("/proc")/str::numstring(pid_r) );
208 PathInfo status( procdir );
209 MIL << "Checking " << status << endl;
210
211 if ( ! status.isDir() )
212 {
213 DBG << "No such process." << endl;
214 return false;
215 }
216
217 static char buffer[513];
218 buffer[0] = buffer[512] = 0;
219 // man proc(5): /proc/[pid]/cmdline is empty if zombie.
220 if ( std::ifstream( (procdir/"cmdline").c_str() ).read( buffer, 512 ).gcount() > 0 )
221 {
222 _lockerName = buffer;
223 DBG << "Is running: " << _lockerName << endl;
224 return true;
225 }
226
227 DBG << "In zombie state." << endl;
228 return false;
229 }
230
232 {
233 clearerr( _zyppLockFile );
234 fseek( _zyppLockFile, 0, SEEK_SET );
235 long readpid = 0;
236 fscanf( _zyppLockFile, "%ld", &readpid );
237 MIL << "read: Lockfile " << _zyppLockFilePath << " has pid " << readpid << " (our pid: " << getpid() << ") "<< std::endl;
238 return (pid_t)readpid;
239 }
240
242 {
243 clearerr( _zyppLockFile );
244 fseek( _zyppLockFile, 0, SEEK_SET );
245 ftruncate( fileno(_zyppLockFile), 0 );
246 fprintf(_zyppLockFile, "%ld\n", (long)getpid() );
247 fflush( _zyppLockFile );
248 _cleanLock = true; // cleanup on exit
249 MIL << "write: Lockfile " << _zyppLockFilePath << " got pid " << getpid() << std::endl;
250 }
251
256 {
257 _lockerPid = readLockFile();
258 if ( _lockerPid == 0 ) {
259 // no or empty lock file
260 return false;
261 } else if ( _lockerPid == getpid() ) {
262 // keep my own lock
263 return false;
264 } else {
265 // a foreign pid in lock
266 if ( isProcessRunning( _lockerPid ) ) {
267 WAR << _lockerPid << " is running and has a ZYpp lock. Sorry." << std::endl;
268 return true;
269 } else {
270 MIL << _lockerPid << " is dead. Ignoring the existing lock file." << std::endl;
271 return false;
272 }
273 }
274 }
275
276 public:
277
279 {
280 if ( geteuid() != 0 )
281 return false; // no lock as non-root
282
283 // Exception safe access to the lockfile.
284 ScopedGuard closeOnReturn( accessLockFile() );
285 scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
286 return safeCheckIsLocked ();
287 }
288
293 {
294 if ( geteuid() != 0 )
295 return false; // no lock as non-root
296
297 // Exception safe access to the lockfile.
298 ScopedGuard closeOnReturn( accessLockFile() );
299 scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
300 if ( !safeCheckIsLocked() ) {
301 writeLockFile();
302 return false;
303 }
304 return true;
305 }
306
307 };
308
310 namespace
311 {
312 static weak_ptr<ZYpp> _theZYppInstance;
313 static scoped_ptr<ZYppGlobalLock> _theGlobalLock; // on/off in sync with _theZYppInstance
314
315 ZYppGlobalLock & globalLock()
316 {
317 if ( !_theGlobalLock )
318 _theGlobalLock.reset( new ZYppGlobalLock( ZYppFactory::lockfileDir() / "zypp.pid" ) );
319 return *_theGlobalLock;
320 }
321 } //namespace
323
325 //
326 // CLASS NAME : ZYpp
327 //
329
330 ZYpp::ZYpp( const Impl_Ptr & impl_r )
331 : _pimpl( impl_r )
332 {
333 ::zyppintern::repoVariablesReset(); // upon re-acquiring the lock...
334 MIL << "ZYpp is on..." << endl;
335 }
336
338 {
339 _theGlobalLock.reset();
340 MIL << "ZYpp is off..." << endl;
341 }
342
344 //
345 // CLASS NAME : ZYppFactoryException
346 //
348
349 ZYppFactoryException::ZYppFactoryException( std::string msg_r, pid_t lockerPid_r, std::string lockerName_r )
350 : Exception( std::move(msg_r) )
351 , _lockerPid( lockerPid_r )
352 , _lockerName(std::move( lockerName_r ))
353 {}
354
357
359 //
360 // CLASS NAME : ZYppFactory
361 //
363
366
369
372
374 //
376 {
377
378 const auto &makeLockedError = []( pid_t pid, const std::string &lockerName ){
379 const std::string &t = str::form(_("System management is locked by the application with pid %d (%s).\n"
380 "Close this application before trying again."), pid, lockerName.c_str() );
381 return ZYppFactoryException(t, pid, lockerName );
382 };
383
384 ZYpp::Ptr _instance = _theZYppInstance.lock();
385 if ( ! _instance )
386 {
387 if ( geteuid() != 0 )
388 {
389 MIL << "Running as user. Skip creating " << globalLock().zyppLockFilePath() << std::endl;
390 }
392 {
393 MIL << "ZYPP_READONLY active." << endl;
394 }
395 else if ( globalLock().zyppLocked() )
396 {
397 bool failed = true;
398 // bsc#1184399,1213231: A negative ZYPP_LOCK_TIMEOUT will wait forever.
399 const long LOCK_TIMEOUT = str::strtonum<long>( getenv( "ZYPP_LOCK_TIMEOUT" ) );
400 if ( LOCK_TIMEOUT != 0 )
401 {
402 Date logwait = Date::now();
403 Date giveup; /* 0 = forever */
404 if ( LOCK_TIMEOUT > 0 ) {
405 giveup = logwait+LOCK_TIMEOUT;
406 MIL << "$ZYPP_LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec. Waiting for the zypp lock until " << giveup << endl;
407 }
408 else
409 MIL << "$ZYPP_LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec. Waiting for the zypp lock..." << endl;
410
411 unsigned delay = 0;
412 do {
413 if ( delay < 60 )
414 delay += 1;
415 else {
416 Date now { Date::now() };
417 if ( now - logwait > Date::day ) {
418 WAR << "$ZYPP_LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec. Another day has passed waiting for the zypp lock..." << endl;
419 logwait = now;
420 }
421 }
422 sleep( delay );
423 {
424 zypp::base::LogControl::TmpLineWriter shutUp; // be quiet
425 failed = globalLock().zyppLocked();
426 }
427 } while ( failed && ( not giveup || Date::now() <= giveup ) );
428
429 if ( failed ) {
430 MIL << "$ZYPP_LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec. Gave up waiting for the zypp lock." << endl;
431 }
432 else {
433 MIL << "$ZYPP_LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec. Finally got the zypp lock." << endl;
434 }
435 }
436 if ( failed )
437 ZYPP_THROW( makeLockedError( globalLock().lockerPid(), globalLock().lockerName() ));
438
439 // we got the global lock, now make sure zypp-rpm is not still running
440 {
441 ZYppGlobalLock zyppRpmLock( ZYppFactory::lockfileDir() / "zypp-rpm.pid" );
442 if ( zyppRpmLock.isZyppLocked () ) {
443 // release global lock, we will exit now
444 _theGlobalLock.reset();
445 ZYPP_THROW( makeLockedError( zyppRpmLock.lockerPid(), zyppRpmLock.lockerName() ));
446 }
447 }
448 }
449
450 // Here we go...
451 static ZYpp::Impl_Ptr _theImplInstance; // for now created once
452 if ( !_theImplInstance )
453 _theImplInstance.reset( new ZYpp::Impl );
454 _instance.reset( new ZYpp( _theImplInstance ) );
455 _theZYppInstance = _instance;
456 }
457
458 return _instance;
459 }
460
462 //
464 { return !_theZYppInstance.expired(); }
465
470
471 /******************************************************************
472 **
473 ** FUNCTION NAME : operator<<
474 ** FUNCTION TYPE : std::ostream &
475 */
476 std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
477 {
478 return str << "ZYppFactory";
479 }
480
482} // namespace zypp
Store and operate on date (time_t).
Definition Date.h:33
static const ValueType day
Definition Date.h:44
static Date now()
Return the current time.
Definition Date.h:78
Base class for Exception.
Definition Exception.h:147
ZYppFactoryException(std::string msg_r, pid_t lockerPid_r, std::string lockerName_r)
ZYpp factory class (Singleton)
Definition ZYppFactory.h:44
static ZYppFactory instance()
Singleton ctor.
bool haveZYpp() const
Whether the ZYpp instance is already created.
ZYpp::Ptr getZYpp() const
ZYppFactory()
Default ctor.
static zypp::Pathname lockfileDir()
Our broken global lock.
const std::string & lockerName() const
ZYppGlobalLock & operator=(const ZYppGlobalLock &)=delete
void _closeLockFile()
Use accessLockFile.
shared_ptr< void > ScopedGuard
std::string _lockerName
file_lock _zyppLockFileLock
void _openLockFile()
Use accessLockFile.
pid_t lockerPid() const
bool zyppLocked()
Try to aquire a lock.
ScopedGuard accessLockFile()
Exception safe access to the lockfile.
const Pathname & zyppLockFilePath() const
bool isProcessRunning(pid_t pid_r)
ZYppGlobalLock & operator=(ZYppGlobalLock &&)=delete
ZYppGlobalLock(const ZYppGlobalLock &)=delete
ZYppGlobalLock(ZYppGlobalLock &&)=delete
ZYppGlobalLock(Pathname &&lFilePath)
shared_ptr< Impl > Impl_Ptr
Definition ZYpp.h:161
::boost::shared_ptr< ZYpp > Ptr
Definition ZYpp.h:63
ZYpp(const Impl_Ptr &impl_r)
Factory ctor.
static LogControl instance()
Singleton access.
Definition LogControl.h:102
void emergencyShutdown()
will cause the log thread to exit and flush all sockets
Wrapper class for stat/lstat.
Definition PathInfo.h:222
const char * c_str() const
String representation.
Definition Pathname.h:112
const std::string & asString() const
String representation.
Definition Pathname.h:93
Signal handler logging a stack trace.
static void backtraceHandler(int sig)
Definition Arch.h:364
String related utilities and Regular expression matching.
Namespace intended to collect all environment variables we use.
Definition Env.h:23
Pathname ZYPP_LOCKFILE_ROOT()
Hack to circumvent the currently poor –root support.
int assert_dir(const Pathname &path, unsigned mode)
Like 'mkdir -p'.
Definition PathInfo.cc:324
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:37
TInt strtonum(const C_Str &str)
Parsing numbers from string.
void IWantIt() ZYPP_DEPRECATED
Easy-to use interface to the ZYPP dependency resolver.
std::ostream & operator<<(std::ostream &str, const SerialNumber &obj)
const Arch Arch_armv7hnl Arch_armv7nhl ZYPP_API
Definition Arch.h:247
std::ostream & dumpBacktrace(std::ostream &stream_r)
Dump current stack trace to a stream.
Definition Backtrace.cc:24
void repoVariablesReset()
Exchange LineWriter for the lifetime of this object.
Definition LogControl.h:191
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition Exception.h:424
#define _(MSG)
Definition Gettext.h:39
#define DBG
Definition Logger.h:97
#define MIL
Definition Logger.h:98
#define WAR
Definition Logger.h:99
#define INT
Definition Logger.h:102
Interface to gettext.