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