libassa  3.5.1
Public Member Functions | Private Member Functions | Private Attributes | List of all members
ASSA::PidFileLock Class Reference

#include <PidFileLock.h>

Inheritance diagram for ASSA::PidFileLock:

Public Member Functions

 PidFileLock ()
 Constructor. More...
 
 ~PidFileLock ()
 Destructor. More...
 
bool lock (const string &filename_)
 Lock the file. More...
 
int get_error () const
 Return last errno value. More...
 
const char * get_error_msg () const
 In case of error, return a verbal description of the last error. More...
 
void dump ()
 Write the state of the lock to debug file. More...
 

Private Member Functions

pid_t open_pid_file (const std::string &fname_)
 Open pid file in a cross-platform way. More...
 
int lock_region ()
 Lock the entire file. More...
 
int lock_region_exclusive ()
 Lock the entire file (only under Cygwin). More...
 
int unlock_region ()
 Unlock the entire file. More...
 
int get_lock_status ()
 Retrieve lock status. More...
 
int write_pid ()
 Write our process pid to the lock file. More...
 
pid_t test_region ()
 Test if file is unlocked. More...
 
void log_error (const char *msg_)
 Log an error message to the log file and set internal error to errno. More...
 

Private Attributes

string m_filename
 Lock file name. More...
 
int m_fd
 Lock file descriptor. More...
 
int m_error
 Last system call error. More...
 
string m_error_msg
 Error explanation. More...
 

Detailed Description

Definition at line 43 of file PidFileLock.h.

Constructor & Destructor Documentation

◆ PidFileLock()

PidFileLock::PidFileLock ( )

Constructor.

Definition at line 31 of file PidFileLock.cpp.

32  :
33  m_fd (-1),
34  m_error (0),
35  m_error_msg ("no errors")
36 {
37  trace_with_mask ("PidFileLock::PidFileLock", PIDFLOCK);
38 
39  l_whence = SEEK_SET;
40  l_start = l_len = l_pid = 0;
41 }
#define trace_with_mask(s, m)
trace_with_mask() is used to trace function call chain in C++ program.
Definition: Logger.h:437
int m_error
Last system call error.
Definition: PidFileLock.h:126
string m_error_msg
Error explanation.
Definition: PidFileLock.h:129
int m_fd
Lock file descriptor.
Definition: PidFileLock.h:123
@ PIDFLOCK
Class PidFileLock messages
Definition: LogMask.h:35

References ASSA::PIDFLOCK, and trace_with_mask.

◆ ~PidFileLock()

PidFileLock::~PidFileLock ( )

Destructor.

If process is holds the lock on the file, file is removed from the file system.

Definition at line 43 of file PidFileLock.cpp.

45 {
46  trace_with_mask ("PidFileLock::~PidFileLock", PIDFLOCK);
47 
48  if (m_fd != -1) {
49  if (unlock_region () == 0) { // if we had a lock
50  DL((PIDFLOCK,"PID file unlocked.\n"));
51 
52  unlink (m_filename.c_str ());
53  DL((PIDFLOCK,"PID file removed.\n"));
54  }
55  close (m_fd);
56  DL((PIDFLOCK,"PID lock file closed.\n"));
57  }
58 }
#define DL(X)
A macro for writing debug message to the Logger.
Definition: Logger.h:273
string m_filename
Lock file name.
Definition: PidFileLock.h:120
int unlock_region()
Unlock the entire file.

References DL, m_fd, m_filename, ASSA::PIDFLOCK, trace_with_mask, and unlock_region().

Member Function Documentation

◆ dump()

void PidFileLock::dump ( void  )

Write the state of the lock to debug file.

m_fd = -1 indicates that lock file is not opened.

Definition at line 383 of file PidFileLock.cpp.

385 {
386  trace_with_mask("PidFileLock::dump", PIDFLOCK);
387 
388 #if !defined (WIN32)
389 
390  DL((PIDFLOCK,"m_filename : \"%s\"\n", m_filename.c_str()));
391  DL((PIDFLOCK,"m_error : %d\n", get_error ()));
392  DL((PIDFLOCK,"m_error_msg: \"%s\"\n", get_error_msg ()));
393  DL((PIDFLOCK,"m_fd : %d\n", m_fd));
394 
395  if (m_fd == -1) return;
396 
397  test_region ();
398 
399  if (this->l_type == F_RDLCK)
400  DL((PIDFLOCK,"l_type : F_RDLCK"));
401 
402  if (this->l_type == F_WRLCK)
403  DL((PIDFLOCK,"l_type : F_WRLCK"));
404 
405  if (this->l_type == F_UNLCK)
406  DL((PIDFLOCK,"l_type : F_UNLCK"));
407 
408  DL((PIDFLOCK,"l_whence : %s\n",
409  this->l_whence == SEEK_SET ? "SEEK_SET" :
410  this->l_whence == SEEK_CUR ? "SEEK_CUR" : "SEEK_END"));
411 
412  DL((PIDFLOCK,"l_start : %d\n", this->l_start));
413  DL((PIDFLOCK,"l_len : %d\n", this->l_len ));
414  DL((PIDFLOCK,"l_pid : %ld\n", this->l_pid ));
415 
416 #endif // !def WIN32
417 }
const char * get_error_msg() const
In case of error, return a verbal description of the last error.
Definition: PidFileLock.h:141
int get_error() const
Return last errno value.
Definition: PidFileLock.h:134
pid_t test_region()
Test if file is unlocked.

References DL, get_error(), get_error_msg(), m_fd, m_filename, ASSA::PIDFLOCK, test_region(), and trace_with_mask.

◆ get_error()

int ASSA::PidFileLock::get_error ( ) const
inline

Return last errno value.

Returns
0 for success, errno on error

Definition at line 133 of file PidFileLock.h.

135 {
136  return m_error;
137 }

References m_error.

Referenced by dump(), and lock().

◆ get_error_msg()

const char * ASSA::PidFileLock::get_error_msg ( ) const
inline

In case of error, return a verbal description of the last error.

Definition at line 140 of file PidFileLock.h.

142 {
143  return m_error_msg.c_str ();
144 }

References m_error_msg.

Referenced by dump(), and ASSA::GenServer::init_internals().

◆ get_lock_status()

int PidFileLock::get_lock_status ( )
private

Retrieve lock status.

Read the file descriptor's flags.

Returns
-1 on error if failed, 0 on success.
On POSIX-compliant systems, on input to fcntl(F_GETLK), the l_type

describes a lock we would like to place on the file. If the lock could be placed, fcntl() does not actually place it, but returns F_UNLCK in the l_type and leaves the other fields of the structure unchanged. If, however, one or more incompatable locks would prevent this lock being placed, then fcntl() returns details about one of these locks in the l_type/l_whence/l_start/l_len fields and sets l_pid to be the PID of the process holding that lock. A lock can be removed explicitly with F_UNLCK or released automatically when the process terminates or if it closes any file descriptor referring to a file on which locks are held.

CYGWIN port does not support F_GETLK command with fcntl() because:

1) LockFileEx() is not implemented on 9x/ME 2) Lock requests given to LockFileEx() may not overlap existing locked regions of the file. 3) There is not nearly a functionality similar to F_GETLK in the Win32 API.

Instead, we try to set a lock. We might fail even if we already hold the lock ourselves. If we fail, try to unlock the file, and if that fails, then we know that file is locked by someone else. This method is not reliable, of course, but that's the best we can do.

Definition at line 293 of file PidFileLock.cpp.

295 {
296  trace_with_mask ("PidFileLock::get_lock_status", PIDFLOCK);
297  int ret;
298 
299 #if defined (WIN32)
300  return 0;
301 #else
302 
303 #ifndef __CYGWIN__ // POSIX-compliant locking
304 
305  this->l_type = F_WRLCK;
306  this->l_start = 0;
307  this->l_whence = SEEK_SET;
308  this->l_len = 0;
309 
310  ret = ::fcntl (m_fd, F_GETLK, static_cast<struct flock*>(this));
311 
312  DL((PIDFLOCK,"fcntl(fd=%d, F_GETLK, %s) returned: %d\n",
313  m_fd,
314  (this->l_type == F_RDLCK ? "F_RDLCK" : "F_WRLCK"),
315  ret));
316  if (ret < 0) {
317  EL ((PIDFLOCK,"fcntl() failed. l_pid = %d\n", this->l_pid));
318  }
319  return (ret);
320 
321 #else // CYGWIN
322 
323  if (lock_region_exclusive () < 0) { // why exclusive?
324  if (unlock_region () < 0) { // already locked
325  char buf[64];
326  pid_t pid; // someone else got it
327  this->l_type = F_RDLCK;
328  if (read (m_fd, buf, 64) > 0) {
329  if (sscanf (buf, "%d", &pid) == 1) {
330  this->l_pid = pid;
331  }
332  }
333  else {
334  this->l_pid = 1; // no real PID information
335  }
336  }
337  }
338  else {
339  unlock_region (); // return the lock into its prestine state
340  }
341  return (0);
342 
343 #endif // !def CYGWIN
344 
345 #endif // !def WIN32
346 
347 }
#define EL(X)
A macro for writing error message to the Logger.
Definition: Logger.h:285
int lock_region_exclusive()
Lock the entire file (only under Cygwin).

References DL, EL, lock_region_exclusive(), m_fd, ASSA::PIDFLOCK, trace_with_mask, and unlock_region().

Referenced by test_region().

◆ lock()

bool PidFileLock::lock ( const string &  filename_)

Lock the file.

Returns
true on success, false on error

Now that we have the lock, truncate file to zero length

Store our PID in the file

Set close-on-exec flag

Definition at line 61 of file PidFileLock.cpp.

63 {
64  trace_with_mask ("PidFileLock::lock", PIDFLOCK);
65 
66 #if defined(WIN32)
67  return true;
68 #else
69  int val;
70  int len;
71  m_filename = Utils::strenv (fname_.c_str ());
72  val = len = 0;
73 
74  DL((PIDFLOCK,"PID lock file: \"%s\"\n", m_filename.c_str ()));
75 
76  if (open_pid_file (m_filename) < 0) {
77  goto done;
78  }
79  DL((PIDFLOCK,"PID lock file opened and locked (fd=%d).\n", m_fd));
80 
83  if (ftruncate (m_fd, 0) < 0) {
84  log_error("ftruncate() error");
85  goto done;
86  }
87  DL((PIDFLOCK,"PID lock file truncated.\n"));
88 
91  if (write_pid () < 0) {
92  log_error("write(PID) error");
93  goto done;
94  }
95 
98  if ((val = ::fcntl(m_fd, F_GETFD, 0)) < 0) {
99  log_error("fcntl(F_GETFD) error");
100  goto done;
101  }
102  val |= FD_CLOEXEC;
103 
104  if (::fcntl (m_fd, F_SETFD, val) < 0) {
105  log_error("fcntl(F_SETFD) error");
106  goto done;
107  }
108  DL((PIDFLOCK,"CLOSE-ON-EXEC is set on FD.\n"));
109 
110  done:
111  if (get_error () != 0) {
112  ::close (m_fd);
113  m_fd = -1;
114  }
115  return m_error == 0 ? true : false;
116 
117 #endif // !def WIN32
118 }
int write_pid()
Write our process pid to the lock file.
pid_t open_pid_file(const std::string &fname_)
Open pid file in a cross-platform way.
void log_error(const char *msg_)
Log an error message to the log file and set internal error to errno.
std::string strenv(const char *in_)
Expand the passed string in_ by substituting environment variable names for their values.

References DL, get_error(), log_error(), m_error, m_fd, m_filename, open_pid_file(), ASSA::PIDFLOCK, ASSA::Utils::strenv(), trace_with_mask, and write_pid().

Referenced by ASSA::GenServer::init_internals().

◆ lock_region()

int PidFileLock::lock_region ( )
private

Lock the entire file.

Returns
-1 on error and if file is locked by some other process, errno is set to EAGAIN or EACCES; 0 on success.

Definition at line 177 of file PidFileLock.cpp.

179 {
180  trace_with_mask ("PidFileLock::lock_region", PIDFLOCK);
181  int ret;
182 
183 #if defined (WIN32)
184  return 0;
185 #else
186 
187 #ifdef __CYGWIN__
188  this->l_type = F_RDLCK; // shared lock
189 #else
190  this->l_type = F_WRLCK;
191 #endif
192 
193  this->l_start = 0;
194  this->l_whence = SEEK_SET;
195  this->l_len = 0;
196 
197  ret = ::fcntl (m_fd, F_SETLK, static_cast<struct flock*>(this));
198 
199  DL((PIDFLOCK,"fcntl(fd=%d, F_SETLK, %s) returned: %d\n",
200  m_fd,
201  (this->l_type == F_RDLCK ? "F_RDLCK" : "F_WRLCK"),
202  ret));
203 
204  return (ret);
205 
206 #endif // !def WIN32
207 }

References DL, m_fd, ASSA::PIDFLOCK, and trace_with_mask.

Referenced by open_pid_file(), and write_pid().

◆ lock_region_exclusive()

int PidFileLock::lock_region_exclusive ( )
private

Lock the entire file (only under Cygwin).

Returns
-1 on error and if file is locked by some other process, errno is set to EAGAIN or EACCES; 0 on success.

Definition at line 211 of file PidFileLock.cpp.

213 {
214  trace_with_mask ("PidFileLock::lock_region_exclusive", PIDFLOCK);
215  int ret = 0;
216 
217 #if defined (WIN32)
218  return 0;
219 #else
220 
221 #ifdef __CYGWIN__
222  this->l_type = F_WRLCK; // exclusive lock - read would fail
223  this->l_start = 0;
224  this->l_whence = SEEK_SET;
225  this->l_len = 0;
226 
227  ret = ::fcntl (m_fd, F_SETLK, static_cast<struct flock*>(this));
228 
229  DL((PIDFLOCK,"fcntl(fd=%d, F_SETLK, F_WRLCK) returned: %d\n", m_fd, ret));
230 #endif
231 
232  return (ret);
233 
234 #endif // !def WIN32
235 }

References DL, m_fd, ASSA::PIDFLOCK, and trace_with_mask.

Referenced by get_lock_status(), and write_pid().

◆ log_error()

void PidFileLock::log_error ( const char *  msg_)
private

Log an error message to the log file and set internal error to errno.

Definition at line 420 of file PidFileLock.cpp.

422 {
423  m_error = errno;
424  EL((ASSAERR,
425  "Error: \"Failed to get a lock on PID file - %s\".\n", msg_));
426 }
@ ASSAERR
ASSA and system errors
Definition: LogMask.h:34

References ASSA::ASSAERR, EL, and m_error.

Referenced by lock(), and open_pid_file().

◆ open_pid_file()

pid_t PidFileLock::open_pid_file ( const std::string &  fname_)
private

Open pid file in a cross-platform way.

Cygwin doesn't implement file locking via fcntl() - for it we test in one step.

If we cannot get lock status, or already have a lock, or if PID file is already locked by another process, then terminate. Otherwise (file is unlocked), proceed with locking.

Try to set a write lock on the entire file

Definition at line 434 of file PidFileLock.cpp.

436 {
437  trace_with_mask("PidFileLock::open_pid_file", PIDFLOCK);
438 
439 #if !defined (WIN32)
440 
441  m_fd = ::open (fname_.c_str (), O_WRONLY|O_CREAT, 0644);
442  if (m_fd < 0) {
443  log_error("open() error.");
444  return -1;
445  }
446 
451  pid_t owner_pid;
452  if ((owner_pid = test_region ()) > 0) {
453  log_error ("PID file is already locked (by someone).");
454  m_error = EPERM;
455  return -1;
456  }
457 
460  if (lock_region () < 0) {
461  if (errno == EACCES || errno == EAGAIN) {
462  log_error("PID file is locked by another process");
463  }
464  else {
465  log_error("write lock error");
466  }
467  return -1;
468  }
469 
470 #endif // !def WIN32
471 
472  return 0;
473 }
int lock_region()
Lock the entire file.

References lock_region(), log_error(), m_error, m_fd, ASSA::PIDFLOCK, test_region(), and trace_with_mask.

Referenced by lock().

◆ test_region()

pid_t PidFileLock::test_region ( )
private

Test if file is unlocked.

Test to see if file is locked by some other process.

Returns
0 if file is unlocked or the pid of the process that holds the lock otherwise.

If it is locked by us, return 0. If it is locked by some other process, return lock owner's PID.

Definition at line 354 of file PidFileLock.cpp.

356 {
357  trace_with_mask ("PidFileLock::test_region", PIDFLOCK);
358  int ret;
359 
360 #if defined (WIN32)
361  return 0;
362 #else
363 
364  ret = get_lock_status ();
365 
366  if (ret < 0) {
367  DL((PIDFLOCK,"Failed to retrieve lock status.\n"));
368  return 1;
369  }
370  if (this->l_type == F_UNLCK) {
371  DL((PIDFLOCK,"Region is not locked.\n"));
372  return(0);
373  }
374 
375  DL((PIDFLOCK,"Region is already locked by PID %d\n", this->l_pid));
376  return (this->l_pid);
377 
378 #endif // !def WIN32
379 }
int get_lock_status()
Retrieve lock status.

References DL, get_lock_status(), ASSA::PIDFLOCK, and trace_with_mask.

Referenced by dump(), and open_pid_file().

◆ unlock_region()

int PidFileLock::unlock_region ( )
private

Unlock the entire file.

Returns
-1 on error; 0 on success.

Definition at line 238 of file PidFileLock.cpp.

240 {
241  trace_with_mask ("PidFileLock::unlock_region", PIDFLOCK);
242  int ret;
243 
244 #if defined (WIN32)
245  return 0;
246 #else
247 
248  this->l_type = F_UNLCK;
249  this->l_start = 0;
250  this->l_whence = SEEK_SET;
251  this->l_len = 0;
252 
253  ret = ::fcntl (m_fd, F_SETLK, static_cast<struct flock*>(this));
254 
255  DL((PIDFLOCK,"fcntl(fd=%d, F_SETLK, F_UNLCK) returned: %d\n",
256  m_fd, ret));
257 
258  return (ret);
259 
260 #endif // !def WIN32
261 }

References DL, m_fd, ASSA::PIDFLOCK, and trace_with_mask.

Referenced by get_lock_status(), write_pid(), and ~PidFileLock().

◆ write_pid()

int PidFileLock::write_pid ( )
private

Write our process pid to the lock file.

Cygwin does not have POSIX semantics for locks.

Returns
-1 on error; 0 on success.

We need to remove shared lock and then get an exclusive lock to write our PID. Then remove the exclusive lock and replace it with the shared lock. This leave two chances for another process to steal the lock from us, but this is the best we can do.

An exclusive lock under Cygwin prevents other processes to even open a file for read-only operations!

Definition at line 131 of file PidFileLock.cpp.

133 {
134  trace_with_mask ("PidFileLock::write_pid", PIDFLOCK);
135 
136 #if defined (WIN32)
137  return 0;
138 #else
139  std::ostringstream mypid;
140  size_t len;
141 
142  this->l_pid = getpid ();
143  mypid << this->l_pid << std::ends;
144  len = strlen (mypid.str ().c_str ());
145 
146 #ifdef __CYGWIN__
147 
148  unlock_region (); // remove shared (weak) lock
150 
151  if (write (m_fd, mypid.str ().c_str (), len) != len) {
152  return -1;
153  }
154  DL((PIDFLOCK,"Wrote PID=%d to the lock file.\n", l_pid));
155  unlock_region (); // give up the exclusive lock
156  lock_region (); // place shared (weak) lock
157 
158 #else // POSIX-compliant locks
159 
160  if (write (m_fd, mypid.str ().c_str (), len) != len) {
161  return -1;
162  }
163  DL((PIDFLOCK,"Wrote PID=%d to the lock file.\n", this->l_pid));
164 
165 #endif
166  return 0;
167 
168 #endif // !def WIN32
169 }
Socket & ends(Socket &os_)
ends manipulator.
Definition: Socket.h:622

References DL, ASSA::ends(), lock_region(), lock_region_exclusive(), m_fd, ASSA::PIDFLOCK, trace_with_mask, and unlock_region().

Referenced by lock().

Member Data Documentation

◆ m_error

int ASSA::PidFileLock::m_error
private

Last system call error.

Definition at line 126 of file PidFileLock.h.

Referenced by get_error(), lock(), log_error(), and open_pid_file().

◆ m_error_msg

string ASSA::PidFileLock::m_error_msg
private

Error explanation.

Definition at line 129 of file PidFileLock.h.

Referenced by get_error_msg().

◆ m_fd

int ASSA::PidFileLock::m_fd
private

◆ m_filename

string ASSA::PidFileLock::m_filename
private

Lock file name.

Definition at line 120 of file PidFileLock.h.

Referenced by dump(), lock(), and ~PidFileLock().


The documentation for this class was generated from the following files: