Provide name of the lock holder in ZYppFactoryException (bnc#580513)
[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
19 #include "zypp/base/Logger.h"
20 #include "zypp/base/Gettext.h"
21 #include "zypp/base/IOStream.h"
22 #include "zypp/PathInfo.h"
23
24 #include "zypp/ZYppFactory.h"
25 #include "zypp/zypp_detail/ZYppImpl.h"
26 #include "zypp/zypp_detail/ZYppReadOnlyHack.h"
27
28 using std::endl;
29
30 ///////////////////////////////////////////////////////////////////
31 namespace zypp
32 { /////////////////////////////////////////////////////////////////
33
34   namespace env
35   {
36     /** Hack to circumvent the currently poor --root support. */
37     inline Pathname ZYPP_LOCKFILE_ROOT()
38     { return getenv("ZYPP_LOCKFILE_ROOT") ? getenv("ZYPP_LOCKFILE_ROOT") : "/"; }
39   }
40
41   ///////////////////////////////////////////////////////////////////
42   namespace zypp_readonly_hack
43   { /////////////////////////////////////////////////////////////////
44
45     static bool active = false;
46
47     void IWantIt()
48     {
49       active = true;
50       MIL << "ZYPP_READONLY promised." <<  endl;
51     }
52
53     /////////////////////////////////////////////////////////////////
54   } // namespace zypp_readonly_hack
55   ///////////////////////////////////////////////////////////////////
56
57   ///////////////////////////////////////////////////////////////////
58   //
59   //    CLASS NAME : ZYppGlobalLock
60   //
61   ///////////////////////////////////////////////////////////////////
62
63   class ZYppGlobalLock
64   {
65     public:
66
67       ZYppGlobalLock()
68       : _clean_lock(false)
69       , _zyppLockFilePath( env::ZYPP_LOCKFILE_ROOT() / "/var/run/zypp.pid" )
70       , _zypp_lockfile(0)
71       , _locker_pid(0)
72     {
73       filesystem::assert_dir(_zyppLockFilePath.dirname());
74     }
75
76     ~ZYppGlobalLock()
77     {
78       try
79         {
80           pid_t curr_pid = getpid();
81           if ( _zypp_lockfile )
82             {
83               unLockFile();
84               closeLockFile();
85
86               if ( _clean_lock )
87               {
88                 MIL << "Cleaning lock file. (" << curr_pid << ")" << std::endl;
89                 if ( filesystem::unlink(_zyppLockFilePath) == 0 )
90                   MIL << "Lockfile cleaned. (" << curr_pid << ")" << std::endl;
91                 else
92                   ERR << "Cant clean lockfile. (" << curr_pid << ")" << std::endl;
93               }
94             }
95         }
96       catch(...) {} // let no exception escape.
97     }
98
99     pid_t locker_pid() const
100     { return _locker_pid; }
101
102     const std::string & locker_name() const
103     { return _locker_name; }
104
105
106     bool _clean_lock;
107
108     private:
109     Pathname _zyppLockFilePath;
110     FILE *_zypp_lockfile;
111     pid_t _locker_pid;
112     std::string _locker_name;
113
114     void openLockFile(const char *mode)
115     {
116
117       _zypp_lockfile = fopen(_zyppLockFilePath.asString().c_str(), mode);
118       if (_zypp_lockfile == 0)
119         ZYPP_THROW (Exception( "Cant open " + _zyppLockFilePath.asString() + " in mode " + std::string(mode) ) );
120     }
121
122     void closeLockFile()
123     {
124       fclose(_zypp_lockfile);
125     }
126
127     void shLockFile()
128     {
129       int fd = fileno(_zypp_lockfile);
130       int lock_error = flock(fd, LOCK_SH);
131       if (lock_error != 0)
132         ZYPP_THROW (Exception( "Cant get shared lock"));
133       else
134         MIL << "locked (shared)" << std::endl;
135     }
136
137     void exLockFile()
138     {
139       int fd = fileno(_zypp_lockfile);
140     // lock access to the file
141       int lock_error = flock(fd, LOCK_EX);
142       if (lock_error != 0)
143         ZYPP_THROW (Exception( "Cant get exclusive lock" ));
144       else
145         MIL << "locked (exclusive)" << std::endl;
146     }
147
148     void unLockFile()
149     {
150       int fd = fileno(_zypp_lockfile);
151     // lock access to the file
152       int lock_error = flock(fd, LOCK_UN);
153       if (lock_error != 0)
154         ZYPP_THROW (Exception( "Cant release lock" ));
155       else
156         MIL << "unlocked" << std::endl;
157     }
158
159     bool lockFileExists()
160     {
161       // check if the file already existed.
162       PathInfo pi(_zyppLockFilePath);
163       DBG << pi << endl;
164       return pi.isExist();
165     }
166
167     void createLockFile()
168     {
169       pid_t curr_pid = getpid();
170       openLockFile("w");
171       exLockFile();
172       fprintf(_zypp_lockfile, "%ld\n", (long) curr_pid);
173       fflush(_zypp_lockfile);
174       unLockFile();
175       MIL << "written lockfile with pid " << curr_pid << std::endl;
176       closeLockFile();
177     }
178
179     bool isProcessRunning(pid_t pid_r)
180     {
181       // it is another program, not me, see if it is still running
182       Pathname procdir( "/proc"/str::numstring(pid_r) );
183       PathInfo status( procdir );
184       MIL << "Checking " <<  status << endl;
185
186       if ( ! status.isDir() )
187       {
188         DBG << "No such process." << endl;
189         return false;
190       }
191
192       static char buffer[513];
193       buffer[0] = buffer[512] = 0;
194       // man proc(5): /proc/[pid]/cmdline is empty if zombie.
195       if ( std::ifstream( (procdir/"cmdline").c_str() ).read( buffer, 512 ).gcount() > 0 )
196       {
197         _locker_name = buffer;
198         DBG << "Is running: " <<  _locker_name << endl;
199         return true;
200       }
201
202       DBG << "In zombie state." << endl;
203       return false;
204     }
205
206     pid_t lockerPid()
207     {
208       pid_t curr_pid = getpid();
209       pid_t locker_pid = 0;
210       long readpid = 0;
211
212       fscanf(_zypp_lockfile, "%ld", &readpid);
213       MIL << "read: Lockfile " << _zyppLockFilePath << " has pid " << readpid << " (our pid: " << curr_pid << ") "<< std::endl;
214       locker_pid = (pid_t) readpid;
215       return locker_pid;
216     }
217
218   public:
219
220     bool zyppLocked()
221     {
222       pid_t curr_pid = getpid();
223
224       if ( lockFileExists() )
225       {
226         MIL << "found lockfile " << _zyppLockFilePath << std::endl;
227         openLockFile("r");
228         shLockFile();
229
230         pid_t locker_pid = lockerPid();
231         _locker_pid = locker_pid;
232         if ( locker_pid == curr_pid )
233         {
234         // alles ok, we are requesting the instance again
235           //MIL << "Lockfile found, but it is myself. Assuming same process getting zypp instance again." << std::endl;
236           return false;
237         }
238         else
239         {
240           if ( isProcessRunning(locker_pid) )
241           {
242             if ( geteuid() == 0 )
243             {
244               // i am root
245               MIL << locker_pid << " is running and has a ZYpp lock. Sorry" << std::endl;
246               return true;
247             }
248             else
249             {
250               MIL << locker_pid << " is running and has a ZYpp lock. Access as normal user allowed." << std::endl;
251               return false;
252             }
253           }
254           else
255           {
256             if ( geteuid() == 0 )
257             {
258               MIL << locker_pid << " has a ZYpp lock, but process is not running. Cleaning lock file." << std::endl;
259               if ( filesystem::unlink(_zyppLockFilePath) == 0 )
260               {
261                 createLockFile();
262               // now open it for reading
263                 openLockFile("r");
264                 shLockFile();
265                 return false;
266               }
267               else
268               {
269                 ERR << "Can't clean lockfile. Sorry, can't create a new lock. Zypp still locked." << std::endl;
270                 return true;
271               }
272             }
273             else
274             {
275               MIL << locker_pid << " is running and has a ZYpp lock. Access as normal user allowed." << std::endl;
276               return false;
277             }
278           }
279         }
280       }
281       else
282       {
283         MIL << "no lockfile " << _zyppLockFilePath << " found" << std::endl;
284         if ( geteuid() == 0 )
285         {
286           MIL << "running as root. Will attempt to create " << _zyppLockFilePath << std::endl;
287           createLockFile();
288         // now open it for reading
289           openLockFile("r");
290           shLockFile();
291         }
292         else
293         {
294           MIL << "running as user. Skipping creating " << _zyppLockFilePath << std::endl;
295         }
296         return false;
297       }
298       return true;
299     }
300
301   };
302
303   namespace
304   {
305     ZYppGlobalLock globalLock;
306     bool           _haveZYpp = false;
307   }
308
309   ///////////////////////////////////////////////////////////////////
310   //
311   //    CLASS NAME : ZYppFactoryException
312   //
313   ///////////////////////////////////////////////////////////////////
314
315   ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r )
316     : Exception( msg_r )
317     , _lockerPid( lockerPid_r )
318     , _lockerName( lockerName_r )
319   {}
320
321   ZYppFactoryException::~ZYppFactoryException() throw ()
322   {}
323
324   ///////////////////////////////////////////////////////////////////
325   //
326   //    CLASS NAME : ZYppFactory
327   //
328   ///////////////////////////////////////////////////////////////////
329
330   ///////////////////////////////////////////////////////////////////
331   //
332   //    METHOD NAME : ZYppFactory::instance
333   //    METHOD TYPE : ZYppFactory
334   //
335   ZYppFactory ZYppFactory::instance()
336   {
337     return ZYppFactory();
338   }
339
340   ///////////////////////////////////////////////////////////////////
341   //
342   //    METHOD NAME : ZYppFactory::ZYppFactory
343   //    METHOD TYPE : Ctor
344   //
345   ZYppFactory::ZYppFactory()
346   {
347
348   }
349
350   ///////////////////////////////////////////////////////////////////
351   //
352   //    METHOD NAME : ZYppFactory::~ZYppFactory
353   //    METHOD TYPE : Dtor
354   //
355   ZYppFactory::~ZYppFactory()
356   {}
357
358   ///////////////////////////////////////////////////////////////////
359   //
360   ZYpp::Ptr ZYppFactory::getZYpp() const
361   {
362     static ZYpp::Ptr _instance;
363
364     if ( ! _instance )
365     {
366       /*--------------------------------------------------*/
367       if ( zypp_readonly_hack::active )
368       {
369           _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) );
370           MIL << "ZYPP_READONLY active." << endl;
371       }
372       /*--------------------------------------------------*/
373       else if ( globalLock.zyppLocked() )
374       {
375         std::string t = str::form(_("System management is locked by the application with pid %d (%s).\n"
376                                      "Close this application before trying again."),
377                                   globalLock.locker_pid(),
378                                   globalLock.locker_name().c_str()
379                                  );
380         ZYPP_THROW(ZYppFactoryException(t, globalLock.locker_pid(),globalLock.locker_name() ));
381       }
382       else
383       {
384         _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) );
385         globalLock._clean_lock = true;
386       }
387
388       if ( _instance )
389         _haveZYpp = true;
390     }
391
392     return _instance;
393   }
394
395   ///////////////////////////////////////////////////////////////////
396   //
397   bool ZYppFactory::haveZYpp() const
398   { return _haveZYpp; }
399
400   /******************************************************************
401   **
402   **    FUNCTION NAME : operator<<
403   **    FUNCTION TYPE : std::ostream &
404   */
405   std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
406   {
407     return str << "ZYppFactory";
408   }
409
410   /////////////////////////////////////////////////////////////////
411 } // namespace zypp
412 ///////////////////////////////////////////////////////////////////