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