45fc1c28bd7f9d41bf174ed680abb72a9890ca77
[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   namespace
286   {
287     static ZYppGlobalLock & globalLock()
288     {
289       static ZYppGlobalLock lock;
290       return lock;
291     }
292     bool           _haveZYpp = false;
293   }
294
295   ///////////////////////////////////////////////////////////////////
296   //
297   //    CLASS NAME : ZYppFactoryException
298   //
299   ///////////////////////////////////////////////////////////////////
300
301   ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r )
302     : Exception( msg_r )
303     , _lockerPid( lockerPid_r )
304     , _lockerName( lockerName_r )
305   {}
306
307   ZYppFactoryException::~ZYppFactoryException() throw ()
308   {}
309
310   ///////////////////////////////////////////////////////////////////
311   //
312   //    CLASS NAME : ZYppFactory
313   //
314   ///////////////////////////////////////////////////////////////////
315
316   ///////////////////////////////////////////////////////////////////
317   //
318   //    METHOD NAME : ZYppFactory::instance
319   //    METHOD TYPE : ZYppFactory
320   //
321   ZYppFactory ZYppFactory::instance()
322   {
323     return ZYppFactory();
324   }
325
326   ///////////////////////////////////////////////////////////////////
327   //
328   //    METHOD NAME : ZYppFactory::ZYppFactory
329   //    METHOD TYPE : Ctor
330   //
331   ZYppFactory::ZYppFactory()
332   {
333
334   }
335
336   ///////////////////////////////////////////////////////////////////
337   //
338   //    METHOD NAME : ZYppFactory::~ZYppFactory
339   //    METHOD TYPE : Dtor
340   //
341   ZYppFactory::~ZYppFactory()
342   {}
343
344   ///////////////////////////////////////////////////////////////////
345   //
346   ZYpp::Ptr ZYppFactory::getZYpp() const
347   {
348     static ZYpp::Ptr _instance;
349
350     if ( ! _instance )
351     {
352       if ( geteuid() != 0 )
353       {
354         MIL << "Running as user. Skip creating " << globalLock().zyppLockFilePath() << std::endl;
355       }
356       else if ( zypp_readonly_hack::active )
357       {
358         MIL << "ZYPP_READONLY active." << endl;
359       }
360       else if ( globalLock().zyppLocked() )
361       {
362         bool failed = true;
363         const long LOCK_TIMEOUT = str::strtonum<long>( getenv( "ZYPP_LOCK_TIMEOUT" ) );
364         if ( LOCK_TIMEOUT > 0 )
365         {
366           MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within $LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec." << endl;
367           unsigned delay = 1;
368           Pathname procdir( "/proc"/str::numstring(globalLock().lockerPid()) );
369           for ( long i = 0; i < LOCK_TIMEOUT; i += delay )
370           {
371             if ( PathInfo( procdir ).isDir() )  // wait for /proc/pid to disapear
372               sleep( delay );
373             else
374             {
375               MIL << "Retry after " << i << " sec." << endl;
376               failed = globalLock().zyppLocked();
377               if ( failed )
378               {
379                 // another proc locked faster. maybe it ends fast as well....
380                 MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within " << (LOCK_TIMEOUT-i) << " sec." << endl;
381                 procdir = Pathname( "/proc"/str::numstring(globalLock().lockerPid()) );
382               }
383               else
384               {
385                 MIL << "Finally got the lock!" << endl;
386                 break;  // gotcha
387               }
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       _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) );
404       if ( _instance )
405         _haveZYpp = true;
406     }
407
408     return _instance;
409   }
410
411   ///////////////////////////////////////////////////////////////////
412   //
413   bool ZYppFactory::haveZYpp() const
414   { return _haveZYpp; }
415
416   /******************************************************************
417   **
418   **    FUNCTION NAME : operator<<
419   **    FUNCTION TYPE : std::ostream &
420   */
421   std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
422   {
423     return str << "ZYppFactory";
424   }
425
426   /////////////////////////////////////////////////////////////////
427 } // namespace zypp
428 ///////////////////////////////////////////////////////////////////