libzypp  13.10.6
InterProcessMutex.cc
Go to the documentation of this file.
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"
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 
37 {}
38 
39 
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 
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  {
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 
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 
313 {
315  return _options.base + ("zypp-" + _options.name + ".lock");
316 }
317 
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 
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:324
Interface to gettext.
#define MIL
Definition: Logger.h:47
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:320
bool next()
Advance to next line.
Definition: IOStream.cc:72
int readlink(const Pathname &symlink_r, Pathname &target_r)
Like &#39;readlink&#39;.
Definition: PathInfo.cc:853
static const Pathname & defaultLocation()
Definition: TmpPath.cc:157
#define XXX
Definition: Logger.h:45
Simple lineparser: Traverse each line in a file.
Definition: IOStream.h:111
options to alter the mutex behavor
Options(ConsumerType ptype, const std::string &pname="zypp", int ptimeout=-1)
Options for a mutex of type ptype with a given name and timeout.
~InterProcessMutex()
Destructor, gives up the lock on the named resource.
#define ERR
Definition: Logger.h:49
bool write(const Pathname &path_r, const std::string &key_r, const std::string &val_r, const std::string &newcomment_r)
Add or change a value in sysconfig file path_r.
Definition: Sysconfig.cc:80
int unlink(const Pathname &path)
Like &#39;unlink&#39;.
Definition: PathInfo.cc:660
long timeout
Definition: MediaCurl.cc:176
ConsumerType
Processes can be of two types Reader or Writer.
#define LMIL
#define _(MSG)
Return translated text.
Definition: Gettext.h:21
std::string numstring(char n, int w=0)
Definition: String.h:219
std::string form(const char *format,...)
Printf style construction of std::string.
Definition: String.cc:34
Base class for Exception.
Definition: Exception.h:143
InterProcessMutex(const Options &poptions)
Creates a mutex with a name and a timeout.
Assert close called on open filedescriptor.
Definition: Fd.h:44
std::string strerror(int errno_r)
Return string describing the error_r code.
Definition: String.cc:51