- ZYppReadOnly access (workaround).
[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     {}
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     bool _clean_lock;
93
94     private:
95     FILE *_zypp_lockfile;
96
97     void openLockFile(const char *mode)
98     {
99       Pathname lock_file = Pathname(ZYPP_LOCK_FILE);
100       _zypp_lockfile = fopen(lock_file.asString().c_str(), mode);
101       if (_zypp_lockfile == 0)
102         ZYPP_THROW (Exception( "Cant open " + lock_file.asString() + " in mode " + std::string(mode) ) );
103     }
104
105     void closeLockFile()
106     {
107       fclose(_zypp_lockfile);
108     }
109
110     void shLockFile()
111     {
112       int fd = fileno(_zypp_lockfile);
113       int lock_error = flock(fd, LOCK_SH);
114       if (lock_error != 0)
115         ZYPP_THROW (Exception( "Cant get shared lock"));
116       else
117         MIL << "locked (shared)" << std::endl;
118     }
119
120     void exLockFile()
121     {
122       int fd = fileno(_zypp_lockfile);
123     // lock access to the file
124       int lock_error = flock(fd, LOCK_EX);
125       if (lock_error != 0)
126         ZYPP_THROW (Exception( "Cant get exclusive lock" ));
127       else
128         MIL << "locked (exclusive)" << std::endl;
129     }
130
131     void unLockFile()
132     {
133       int fd = fileno(_zypp_lockfile);
134     // lock access to the file
135       int lock_error = flock(fd, LOCK_UN);
136       if (lock_error != 0)
137         ZYPP_THROW (Exception( "Cant release lock" ));
138       else
139         MIL << "unlocked" << std::endl;
140     }
141
142     bool lockFileExists()
143     {
144       Pathname lock_file = Pathname(ZYPP_LOCK_FILE);
145     // check if the file already existed.
146       bool exists = PathInfo(lock_file).isExist();
147       return exists;
148     }
149
150     void createLockFile()
151     {
152       pid_t curr_pid = getpid();
153       openLockFile("w");
154       exLockFile();
155       fprintf(_zypp_lockfile, "%ld\n", (long) curr_pid);
156       fflush(_zypp_lockfile);
157       unLockFile();
158       MIL << "written lockfile with pid " << curr_pid << std::endl;
159       closeLockFile();
160     }
161
162     bool isProcessRunning(pid_t pid)
163     {
164     // it is another program, not me, see if it is still running
165       stringstream ss;
166       ss << "/proc/" << pid << "/status";
167       Pathname procfile = Pathname(ss.str());
168       MIL << "Checking " << procfile << " to determine if pid is running: " << pid << std::endl;
169       bool still_running = PathInfo(procfile).isExist();
170       return still_running;
171     }
172
173     pid_t lockerPid()
174     {
175       pid_t locked_pid = 0;
176       long readpid = 0;
177
178       fscanf(_zypp_lockfile, "%ld", &readpid);
179       MIL << "read: Lockfile " << ZYPP_LOCK_FILE << " has pid " << readpid << std::endl;
180       locked_pid = (pid_t) readpid;
181       return locked_pid;
182     }
183
184   public:
185
186     bool zyppLocked()
187     {
188       pid_t curr_pid = getpid();
189       Pathname lock_file = Pathname(ZYPP_LOCK_FILE);
190
191       if ( lockFileExists() )
192       {
193         openLockFile("r");
194         shLockFile();
195
196         pid_t locker_pid = lockerPid();
197         if ( locker_pid == curr_pid )
198         {
199         // alles ok, we are requesting the instance again
200           //MIL << "Lockfile found, but it is myself. Assuming same process getting zypp instance again." << std::endl;
201           return false;
202         }
203         else
204         {
205           if ( isProcessRunning(locker_pid) )
206           {
207             if ( geteuid() == 0 )
208             {
209               // i am root
210               MIL << locker_pid << " is running and has a ZYpp lock. Sorry" << std::endl;
211               return true;
212             }
213             else
214             {
215               MIL << locker_pid << " is running and has a ZYpp lock. Access as normal user allowed." << std::endl;
216               return false;
217             }
218           }
219           else
220           {
221             if ( geteuid() == 0 )
222             {
223               MIL << locker_pid << " has a ZYpp lock, but process is not running. Cleaning lock file." << std::endl;
224               if ( filesystem::unlink(lock_file) == 0 )
225               {
226                 createLockFile();
227               // now open it for reading
228                 openLockFile("r");
229                 shLockFile();
230                 return false;
231               }
232               else
233               {
234                 ERR << "Can't clean lockfile. Sorry, can't create a new lock. Zypp still locked." << std::endl;
235                 return true;
236               }
237             }
238           }
239         }
240       }
241       else
242       {
243         if ( geteuid() == 0 )
244         {
245           createLockFile();
246         // now open it for reading
247           openLockFile("r");
248           shLockFile();
249         }
250         return false;
251       }
252       return true;
253     }
254
255   };
256
257   static ZYppGlobalLock globalLock;
258
259   ///////////////////////////////////////////////////////////////////
260   //
261   //    CLASS NAME : ZYppFactoryException
262   //
263   ///////////////////////////////////////////////////////////////////
264
265   ZYppFactoryException::ZYppFactoryException( const std::string & msg_r )
266   : Exception(N_("Cannot aquire zypp lock."))
267   {}
268
269   ///////////////////////////////////////////////////////////////////
270   //
271   //    CLASS NAME : ZYppFactory
272   //
273   ///////////////////////////////////////////////////////////////////
274
275   ///////////////////////////////////////////////////////////////////
276   //
277   //    METHOD NAME : ZYppFactory::instance
278   //    METHOD TYPE : ZYppFactory
279   //
280   ZYppFactory ZYppFactory::instance()
281   {
282     return ZYppFactory();
283   }
284
285   ///////////////////////////////////////////////////////////////////
286   //
287   //    METHOD NAME : ZYppFactory::ZYppFactory
288   //    METHOD TYPE : Ctor
289   //
290   ZYppFactory::ZYppFactory()
291   {
292
293   }
294
295   ///////////////////////////////////////////////////////////////////
296   //
297   //    METHOD NAME : ZYppFactory::~ZYppFactory
298   //    METHOD TYPE : Dtor
299   //
300   ZYppFactory::~ZYppFactory()
301   {}
302
303   ///////////////////////////////////////////////////////////////////
304   //
305   ZYpp::Ptr ZYppFactory::getZYpp() const
306   {
307     static ZYpp::Ptr _instance;
308
309     if ( ! _instance )
310     {
311       /*--------------------------------------------------*/
312       if ( zypp_readonly_hack::active )
313         {
314           _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) );
315           MIL << "ZYPP_READONLY active." << endl;
316           return _instance;
317         }
318       /*--------------------------------------------------*/
319       if ( globalLock.zyppLocked() )
320       {
321         ZYPP_THROW( ZYppFactoryException(N_("Cannot aquire zypp lock.")) );
322       }
323       else
324       {
325         _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) );
326         globalLock._clean_lock = true;
327       }
328     }
329
330     return _instance;
331   }
332
333   /******************************************************************
334   **
335   **    FUNCTION NAME : operator<<
336   **    FUNCTION TYPE : std::ostream &
337   */
338   std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
339   {
340     return str << "ZYppFactory";
341   }
342
343   /////////////////////////////////////////////////////////////////
344 } // namespace zypp
345 ///////////////////////////////////////////////////////////////////