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