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