- ZYppFactory::getZYpp() throws if lock can't be aquired.
[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
27 #define ZYPP_LOCK_FILE "/var/run/zypp.pid"
28
29 using std::endl;
30 using namespace std;
31
32 ///////////////////////////////////////////////////////////////////
33 namespace zypp
34 { /////////////////////////////////////////////////////////////////
35
36   ///////////////////////////////////////////////////////////////////
37   //
38   //    CLASS NAME : ZYppGlobalLock
39   //
40   ///////////////////////////////////////////////////////////////////
41
42   class ZYppGlobalLock
43   {
44     public:
45
46     ZYppGlobalLock() : _zypp_lockfile(0)
47     {
48
49     }
50
51     ~ZYppGlobalLock()
52     {
53       try
54         {
55           pid_t curr_pid = getpid();
56           if ( _zypp_lockfile )
57             {
58               Pathname lock_file = Pathname(ZYPP_LOCK_FILE);
59               unLockFile();
60               closeLockFile();
61
62               MIL << "Cleaning lock file. (" << curr_pid << ")" << std::endl;
63               if ( filesystem::unlink(lock_file) == 0 )
64                 MIL << "Lockfile cleaned. (" << curr_pid << ")" << std::endl;
65               else
66                 ERR << "Cant clean lockfile. (" << curr_pid << ")" << std::endl;
67             }
68         }
69       catch(...) {} // let no exception escape.
70     }
71
72     private:
73     FILE *_zypp_lockfile;
74
75     void openLockFile(const char *mode)
76     {
77       Pathname lock_file = Pathname(ZYPP_LOCK_FILE);
78       _zypp_lockfile = fopen(lock_file.asString().c_str(), mode);
79       if (_zypp_lockfile == 0)
80         ZYPP_THROW (Exception( "Cant open " + lock_file.asString() + " in mode " + std::string(mode) ) );
81     }
82
83     void closeLockFile()
84     {
85       fclose(_zypp_lockfile);
86     }
87
88     void shLockFile()
89     {
90       int fd = fileno(_zypp_lockfile);
91       int lock_error = flock(fd, LOCK_SH);
92       if (lock_error != 0)
93         ZYPP_THROW (Exception( "Cant get shared lock"));
94       else
95         MIL << "locked (shared)" << std::endl;
96     }
97
98     void exLockFile()
99     {
100       int fd = fileno(_zypp_lockfile);
101     // lock access to the file
102       int lock_error = flock(fd, LOCK_EX);
103       if (lock_error != 0)
104         ZYPP_THROW (Exception( "Cant get exclusive lock" ));
105       else
106         MIL << "locked (exclusive)" << std::endl;
107     }
108
109     void unLockFile()
110     {
111       int fd = fileno(_zypp_lockfile);
112     // lock access to the file
113       int lock_error = flock(fd, LOCK_UN);
114       if (lock_error != 0)
115         ZYPP_THROW (Exception( "Cant release lock" ));
116       else
117         MIL << "unlocked" << std::endl;
118     }
119
120     bool lockFileExists()
121     {
122       Pathname lock_file = Pathname(ZYPP_LOCK_FILE);
123     // check if the file already existed.
124       bool exists = PathInfo(lock_file).isExist();
125       return exists;
126     }
127
128     void createLockFile()
129     {
130       pid_t curr_pid = getpid();
131       openLockFile("w");
132       exLockFile();
133       fprintf(_zypp_lockfile, "%ld\n", (long) curr_pid);
134       fflush(_zypp_lockfile);
135       unLockFile();
136       MIL << "written lockfile with pid " << curr_pid << std::endl;
137       closeLockFile();
138     }
139
140     bool isProcessRunning(pid_t pid)
141     {
142     // it is another program, not me, see if it is still running
143       stringstream ss;
144       ss << "/proc/" << pid << "/status";
145       Pathname procfile = Pathname(ss.str());
146       MIL << "Checking " << procfile << " to determine if pid is running: " << pid << std::endl;
147       bool still_running = PathInfo(procfile).isExist();
148       return still_running;
149     }
150
151     pid_t lockerPid()
152     {
153       pid_t locked_pid = 0;
154       long readpid = 0;
155
156       fscanf(_zypp_lockfile, "%ld", &readpid);
157       MIL << "read: Lockfile " << ZYPP_LOCK_FILE << " has pid " << readpid << std::endl;
158       locked_pid = (pid_t) readpid;
159       return locked_pid;
160     }
161
162   public:
163
164     bool zyppLocked()
165     {
166       pid_t curr_pid = getpid();
167       Pathname lock_file = Pathname(ZYPP_LOCK_FILE);
168
169       if ( lockFileExists() )
170       {
171         openLockFile("r");
172         shLockFile();
173
174         pid_t locker_pid = lockerPid();
175         if ( locker_pid == curr_pid )
176         {
177         // alles ok, we are requesting the instance again
178           //MIL << "Lockfile found, but it is myself. Assuming same process getting zypp instance again." << std::endl;
179           return false;
180         }
181         else
182         {
183           if ( isProcessRunning(locker_pid) )
184           {
185             if ( geteuid() == 0 )
186             {
187               // i am root
188               MIL << locker_pid << " is running and has a ZYpp lock. Sorry" << std::endl;
189               return true;
190             }
191             else
192             {
193               MIL << locker_pid << " is running and has a ZYpp lock. Access as normal user allowed." << std::endl;
194               return false;
195             }
196           }
197           else
198           {
199             if ( geteuid() == 0 )
200             {
201               MIL << locker_pid << " has a ZYpp lock, but process is not running. Cleaning lock file." << std::endl;
202               if ( filesystem::unlink(lock_file) == 0 )
203               {
204                 createLockFile();
205               // now open it for reading
206                 openLockFile("r");
207                 shLockFile();
208                 return false;
209               }
210               else
211               {
212                 ERR << "Can't clean lockfile. Sorry, can't create a new lock. Zypp still locked." << std::endl;
213                 return true;
214               }
215             }
216           }
217         }
218       }
219       else
220       {
221         if ( geteuid() == 0 )
222         {
223           createLockFile();
224         // now open it for reading
225           openLockFile("r");
226           shLockFile();
227         }
228         return false;
229       }
230       return true;
231     }
232
233   };
234
235   static ZYppGlobalLock globalLock;
236
237   ///////////////////////////////////////////////////////////////////
238   //
239   //    CLASS NAME : ZYppFactoryException
240   //
241   ///////////////////////////////////////////////////////////////////
242
243   ZYppFactoryException::ZYppFactoryException( const std::string & msg_r )
244   : Exception(N_("Cannot aquire zypp lock."))
245   {}
246
247   ///////////////////////////////////////////////////////////////////
248   //
249   //    CLASS NAME : ZYppFactory
250   //
251   ///////////////////////////////////////////////////////////////////
252
253   ///////////////////////////////////////////////////////////////////
254   //
255   //    METHOD NAME : ZYppFactory::instance
256   //    METHOD TYPE : ZYppFactory
257   //
258   ZYppFactory ZYppFactory::instance()
259   {
260     return ZYppFactory();
261   }
262
263   ///////////////////////////////////////////////////////////////////
264   //
265   //    METHOD NAME : ZYppFactory::ZYppFactory
266   //    METHOD TYPE : Ctor
267   //
268   ZYppFactory::ZYppFactory()
269   {
270
271   }
272
273   ///////////////////////////////////////////////////////////////////
274   //
275   //    METHOD NAME : ZYppFactory::~ZYppFactory
276   //    METHOD TYPE : Dtor
277   //
278   ZYppFactory::~ZYppFactory()
279   {}
280
281   ///////////////////////////////////////////////////////////////////
282   //
283   ZYpp::Ptr ZYppFactory::getZYpp() const
284   {
285     static ZYpp::Ptr _instance;
286
287     if ( ! _instance )
288     {
289       if ( globalLock.zyppLocked() )
290       {
291         ZYPP_THROW( ZYppFactoryException(N_("Cannot aquire zypp lock.")) );
292       }
293       else
294       {
295         _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) );
296       }
297     }
298
299     return _instance;
300   }
301
302   /******************************************************************
303   **
304   **    FUNCTION NAME : operator<<
305   **    FUNCTION TYPE : std::ostream &
306   */
307   std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
308   {
309     return str << "ZYppFactory";
310   }
311
312   /////////////////////////////////////////////////////////////////
313 } // namespace zypp
314 ///////////////////////////////////////////////////////////////////