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"
15 #include "zypp/TmpPath.h"
16 #include "zypp/Pathname.h"
17 #include "zypp/PathInfo.h"
19 #define LMIL MIL << "LOCK [" << _options.name << "] "
28 ZYppLockedException::ZYppLockedException( const std::string & msg_r,
29 const std::string &name,
32 , _locker_pid (locker_pid)
36 ZYppLockedException::~ZYppLockedException() throw()
40 InterProcessMutex::Options::Options( ConsumerType ptype,
41 const std::string &pname,
50 base = filesystem::TmpPath::defaultLocation() + ( string("zypp-") + getenv("USER") );
55 InterProcessMutex::InterProcessMutex( const Options &poptions )
58 // get the current pid
59 pid_t curr_pid = getpid();
60 Pathname lock_file = lockFilePath();
68 // try to create the lock file atomically, this will fail if
71 _fd.reset( new Fd( lock_file, O_RDWR | O_CREAT | O_EXCL, 0666) );
75 if ( !_fd || !_fd->isOpen() )
79 // the file exists, lets see if someone has it locked exclusively
80 _fd.reset( new Fd( lock_file, O_RDWR ) );
83 ZYPP_THROW(Exception(str::form(_("Can't open lock file: %s"), strerror(errno))));
86 memset(&lock, 0, sizeof(struct flock));
87 lock.l_whence = SEEK_SET;
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 );
95 // get lock information
96 if (fcntl(_fd->fd(), F_GETLK, &lock) < 0)
98 ZYPP_THROW(Exception(string("Error getting lock info: ") + strerror(errno)));
102 MIL << lock_file << " : ";
103 switch ( lock.l_type )
106 MIL << " Write-Lock conflicts" << endl;
109 MIL << " Read-Lock conflicts" << endl;
112 MIL << " No lock conflicts" << endl;
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
132 if ( lock.l_type != F_UNLCK )
134 // some lock conflicts with us.
135 LMIL << "pid " << lock.l_pid << " is running and has a lock that conflicts with us." << std::endl;
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 ) )
142 ZYPP_THROW(ZYppLockedException(
143 _("This action is being run by another program already."),
144 _options.name, lock.l_pid));
147 // if not, let sleep one second and count it
148 LMIL << "waiting 1 second..." << endl;
153 else if ( ( lock.l_type == F_UNLCK ) && ( _options.type == Reader ) )
155 // either there is no lock or a reader has it so we just
156 // acquire a reader lock.
158 // try to get more lock info
159 lock.l_type = F_WRLCK;
161 if (fcntl(_fd->fd(), F_GETLK, &lock) < 0)
163 ZYPP_THROW(Exception(string("Error getting lock info: ") + strerror(errno)));
166 if ( lock.l_type == F_UNLCK )
168 LMIL << "no previous readers, unlinking lock file and retrying." << endl;
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();
181 if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
183 ZYPP_THROW (Exception( "Can't lock file to unlink it."));
185 filesystem::unlink(lock_file.c_str());
188 else if ( lock.l_type == F_RDLCK )
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();
197 if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
199 ZYPP_THROW (Exception( "Can't lock file for reader"));
201 // and keep the lock open.
207 ERR << "impossible condition" << endl;
212 else if ( ( lock.l_type == F_UNLCK ) && ( _options.type == Writer ) )
214 LMIL << "stale lock found" << endl;
215 // Nobody conflicts with a writer lock so nobody is actually
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();
227 if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
229 ZYPP_THROW (Exception( "Can't lock file to unlink it."));
231 filesystem::unlink(lock_file.c_str());
236 // undefined case, just get out of the loop
237 LMIL << "undefined case!" << endl;
245 // exclusive file creation succeeded. So may be we are the
246 // first writer or first reader
248 // try to lock it exclusively
249 // if it fails, someone won us, so we just go for another try
251 LMIL << "no lock found, taking ownership of it as a " << ( (_options.type == Reader ) ? "reader" : "writer" ) << endl;
253 memset(&lock, 0, sizeof(struct flock));
254 lock.l_whence = SEEK_SET;
255 lock.l_type = F_WRLCK;
256 lock.l_pid = getpid();
258 if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
259 ZYPP_THROW (Exception( "Can't lock file to write pid."));
262 sprintf( buffer, "%d\n", curr_pid);
263 write( _fd->fd(), buffer, strlen(buffer));
265 // by now the pid is written and the file locked.
266 // If we are a reader, just downgrade the lock to
268 if ( _options.type == Reader )
270 lock.l_type = F_RDLCK;
272 if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
273 ZYPP_THROW (Exception( "Can't set lock file to shared"));
280 LMIL << "Lock intialized" << endl;
284 InterProcessMutex::~InterProcessMutex()
288 Pathname lock_file = lockFilePath();
290 << ( (_options.type == Reader ) ? "reader" : "writer" )
291 << " lock on " << lock_file << endl;
293 switch ( _options.type )
300 // we are the only writer, so unlink the file
301 filesystem::unlink(lock_file.c_str());
305 // and finally close the file and release the lock
306 // (happens automatically)
308 catch(...) {} // let no exception escape.
312 Pathname InterProcessMutex::lockFilePath() const
314 filesystem::assert_dir(_options.base);
315 return _options.base + ("zypp-" + _options.name + ".lock");
318 bool InterProcessMutex::isProcessRunning(pid_t pid_r)
320 // it is another program, not me, see if it is still running
321 Pathname procdir( Pathname("/proc") / str::numstring(pid_r) );
323 PathInfo status( procdir/"status" );
324 XXX << "Checking " << status << endl;
325 bool still_running = status.isExist();
329 Pathname p( procdir/"exe" );
330 XXX << p << " -> " << filesystem::readlink( p ) << endl;
332 p = procdir/"cmdline";
334 std::ifstream infile( p.c_str() );
335 for( iostr::EachLine in( infile ); in; in.next() )
341 return still_running;