Imported Upstream version 14.32.2
[platform/upstream/libzypp.git] / zypp / base / InterProcessMutex.cc
1
2 extern "C"
3 {
4 #include <sys/file.h>
5 }
6 #include <iostream>
7 #include <fstream>
8
9 #include "zypp/base/Logger.h"
10 #include "zypp/base/Gettext.h"
11 #include "zypp/base/IOStream.h"
12 #include "zypp/base/InterProcessMutex.h"
13 #include "zypp/base/String.h"
14
15 #include "zypp/TmpPath.h"
16 #include "zypp/Pathname.h"
17 #include "zypp/PathInfo.h"
18
19 #define LMIL MIL << "LOCK [" << _options.name << "] "
20
21 using namespace std;
22
23 namespace zypp
24 {
25 namespace base
26 {
27
28 ZYppLockedException::ZYppLockedException( const std::string & msg_r,
29                                           const std::string &name,
30                                           pid_t locker_pid )
31     : Exception(msg_r)
32     , _locker_pid (locker_pid)
33     , _name(name)
34 {}
35
36 ZYppLockedException::~ZYppLockedException() throw()
37 {}
38  
39
40 InterProcessMutex::Options::Options( ConsumerType ptype,
41                                      const std::string &pname,
42                                      int ptimeout )
43     : name(pname) 
44     , timeout(ptimeout)
45     , type(ptype)
46 {
47     if ( geteuid() == 0 )
48         base = "/var/run";
49     else
50         base = filesystem::TmpPath::defaultLocation() + ( string("zypp-") + getenv("USER") );
51 }
52     
53
54    
55 InterProcessMutex::InterProcessMutex( const Options &poptions )
56     : _options(poptions)
57 {
58     // get the current pid
59     pid_t curr_pid = getpid();
60     Pathname lock_file = lockFilePath();
61     int totalslept = 0;
62     int k = 0;
63     
64     while (1)
65     {
66         k++;
67         
68         // try to create the lock file atomically, this will fail if
69         // the lock exists
70         try {
71           _fd.reset( new Fd( lock_file, O_RDWR | O_CREAT | O_EXCL, 0666) );
72         } catch (...) {
73           _fd.reset();
74         }
75         if ( !_fd || !_fd->isOpen() )
76         {
77             struct flock lock;
78             
79             // the file exists, lets see if someone has it locked exclusively
80             _fd.reset( new Fd( lock_file, O_RDWR ) );
81             if ( !_fd->isOpen() )
82             {
83                 ZYPP_THROW(Exception(str::form(_("Can't open lock file: %s"), strerror(errno))));
84             }
85             
86             memset(&lock, 0, sizeof(struct flock));
87             lock.l_whence = SEEK_SET;
88
89             // GETLK tells you the conflicting lock as if the lock you pass
90             // would have been set. So set the lock type depending on whether
91             // we are a writer or a reader.
92             lock.l_type = ( ( _options.type == Writer ) ? F_WRLCK : F_RDLCK );
93             
94
95             // get lock information
96             if (fcntl(_fd->fd(), F_GETLK, &lock) < 0)
97             {
98                 ZYPP_THROW(Exception(string("Error getting lock info: ") +  strerror(errno)));
99             }
100
101             
102             MIL << lock_file << " : ";
103             switch ( lock.l_type )
104             {
105                 case F_WRLCK:
106                     MIL << " Write-Lock conflicts" << endl;
107                     break;
108                 case F_RDLCK:
109                     MIL << " Read-Lock conflicts" << endl;                    
110                     break;
111                 case F_UNLCK:
112                     MIL << " No lock conflicts" << endl;
113                     break;
114                 default:
115                     break;
116                     
117             }
118             
119             // F_GETLK is confusing
120             // http://groups.google.com/group/comp.unix.solaris/tree/browse_frm/month/2005-09/123fae2c774bceed?rnum=61&_done=%2Fgroup%2Fcomp.unix.solaris%2Fbrowse_frm%2Fmonth%2F2005-09%3F
121             // new table of access
122             // F_WRLCK   Reader  Wait or abort
123             // F_WRLCK   Writer  Wait or abort
124             // F_RDLCK   Writer  Wait or abort
125             // F_RDLCK   Reader  Can't happen, anyway, wait or abort            
126             // F_UNLCK   Reader  Take reader lock
127             // F_UNLCK   Writer  Take writer lock
128             
129             
130
131             // wait or abort
132             if (  lock.l_type != F_UNLCK )
133             {
134                 // some lock conflicts with us.
135                 LMIL << "pid " << lock.l_pid << " is running and has a lock that conflicts with us." << std::endl;
136                 
137                 // abort if we have slept more or equal than the timeout, but
138                 // not for the case where timeout is negative which means no
139                 // timeout and therefore we never abort.
140                 if ( (totalslept >= _options.timeout) && (_options.timeout >= 0 ) )
141                 {
142                     ZYPP_THROW(ZYppLockedException(                                       
143                                    _("This action is being run by another program already."),
144                                    _options.name, lock.l_pid));
145                 }
146                         
147                 // if not, let sleep one second and count it
148                 LMIL << "waiting 1 second..." << endl;
149                 sleep(1);
150                 ++totalslept;
151                 continue;
152             }
153             else if ( ( lock.l_type == F_UNLCK ) && ( _options.type == Reader ) )
154             {
155                 // either there is no lock or a reader has it so we just
156                 // acquire a reader lock.
157
158                 // try to get more lock info
159                 lock.l_type = F_WRLCK;
160  
161                 if (fcntl(_fd->fd(), F_GETLK, &lock) < 0)
162                 {
163                     ZYPP_THROW(Exception(string("Error getting lock info: ") +  strerror(errno)));
164                 }
165                 
166                 if ( lock.l_type == F_UNLCK )
167                 {
168                     LMIL << "no previous readers, unlinking lock file and retrying." << endl;
169                     
170                     // actually there are no readers
171                     // lets delete it and break, so the next loop will
172                     // probably succeed in creating it. The worst thing that can
173                     // happen is that another process will take it first, but
174                     // we are not aiming at such level of correctness. Otherwise
175                     // the code path will complicate too much.
176                     memset(&lock, 0, sizeof(struct flock));
177                     lock.l_type = F_WRLCK;
178                     lock.l_whence = SEEK_SET;
179                     lock.l_pid = getpid();
180
181                     if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
182                     {
183                         ZYPP_THROW (Exception( "Can't lock file to unlink it."));
184                     }
185                     filesystem::unlink(lock_file.c_str());
186                     continue;
187                 }
188                 else if ( lock.l_type == F_RDLCK )
189                 {
190                     // there is another reader.
191                     LMIL << "previous readers on lock file. taking lock as a reader." << std::endl;
192                     memset(&lock, 0, sizeof(struct flock));
193                     lock.l_type = F_RDLCK;
194                     lock.l_whence = SEEK_SET;
195                     lock.l_pid = getpid();
196
197                     if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
198                     {
199                         ZYPP_THROW (Exception( "Can't lock file for reader"));
200                     }
201                     // and keep the lock open.
202                     break;
203                 }
204                 else
205                 {
206                     // cant happen!
207                     ERR << "impossible condition" << endl;
208                     
209                     break;
210                 }
211             }
212             else if ( ( lock.l_type == F_UNLCK ) && ( _options.type == Writer ) )
213             {
214                 LMIL << "stale lock found" << endl;
215                 // Nobody conflicts with a writer lock so nobody is actually
216                 // locking.
217                 // lets delete it and break, so the next loop will
218                 // probably succeed in creating it. The worst thing that can
219                 // happen is that another process will take it first, but
220                 // we are not aiming at such level of correctness. Otherwise
221                 // the code path will complicate too much.
222                 memset(&lock, 0, sizeof(struct flock));
223                 lock.l_type = F_WRLCK;
224                 lock.l_whence = SEEK_SET;
225                 lock.l_pid = getpid();
226
227                 if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
228                 {
229                     ZYPP_THROW (Exception( "Can't lock file to unlink it."));
230                 }
231                 filesystem::unlink(lock_file.c_str());
232                 continue;
233             } 
234             else 
235             {
236                 // undefined case, just get out of the loop
237                 LMIL << "undefined case!" << endl;
238                 
239                 break;
240             }
241             
242         }
243         else 
244         {
245             // exclusive file creation succeeded. So may be we are the
246             // first writer or first reader
247             
248             // try to lock it exclusively
249             // if it fails, someone won us, so we just go for another try
250             // or just abort
251             LMIL << "no lock found, taking ownership of it as a " << ( (_options.type == Reader ) ? "reader" : "writer" ) << endl;
252             struct flock lock;
253             memset(&lock, 0, sizeof(struct flock));
254             lock.l_whence = SEEK_SET;
255             lock.l_type = F_WRLCK;
256             lock.l_pid = getpid();
257
258             if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
259                 ZYPP_THROW (Exception( "Can't lock file to write pid."));
260             
261             char buffer[100];
262             sprintf( buffer, "%d\n", curr_pid);
263             write( _fd->fd(), buffer, strlen(buffer));
264             
265             // by now the pid is written and the file locked.
266             // If we are a reader, just downgrade the lock to
267             // read shared lock.
268             if ( _options.type == Reader )
269             {
270                 lock.l_type = F_RDLCK;
271                
272                 if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
273                     ZYPP_THROW (Exception( "Can't set lock file to shared"));
274             }
275             
276             break;
277         }           
278     } // end loop       
279
280     LMIL << "Lock intialized" << endl;
281     
282 }
283
284 InterProcessMutex::~InterProcessMutex()
285 {
286     try
287     {
288         Pathname lock_file = lockFilePath();
289         LMIL << "dropping " 
290              << ( (_options.type == Reader ) ? "reader" : "writer" ) 
291              << " lock on " << lock_file << endl;
292         
293         switch ( _options.type )
294         {
295             case Reader:
296                 
297                 break;
298                 
299             case Writer:
300                 // we are the only writer, so unlink the file
301                 filesystem::unlink(lock_file.c_str());
302                 break;
303                 
304         }
305         // and finally close the file and release the lock
306         // (happens automatically)
307     }
308     catch(...) {} // let no exception escape.
309 }
310
311
312 Pathname InterProcessMutex::lockFilePath() const
313 {
314     filesystem::assert_dir(_options.base);
315     return _options.base + ("zypp-" + _options.name + ".lock");
316 }    
317
318 bool InterProcessMutex::isProcessRunning(pid_t pid_r)
319 {
320     // it is another program, not me, see if it is still running
321     Pathname procdir( Pathname("/proc") / str::numstring(pid_r) );
322
323     PathInfo status( procdir/"status" );
324     XXX << "Checking " <<  status << endl;
325     bool still_running = status.isExist();
326
327     if ( still_running )
328     {
329         Pathname p( procdir/"exe" );
330         XXX << p << " -> " << filesystem::readlink( p ) << endl;
331
332         p = procdir/"cmdline";
333         XXX << p << ": ";
334         std::ifstream infile( p.c_str() );
335         for( iostr::EachLine in( infile ); in; in.next() )
336         {
337           XXX << *in << endl;
338         }
339      }
340
341      return still_running;
342 }
343
344
345 }
346 }
347
348