ASSA::PidFileLock Class Reference

#include <PidFileLock.h>

List of all members.

Public Member Functions

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

Private Member Functions

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

Private Attributes

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


Detailed Description

Definition at line 43 of file PidFileLock.h.


Constructor & Destructor Documentation

PidFileLock::PidFileLock (  ) 

Constructor.

Definition at line 32 of file PidFileLock.cpp.

References ASSA::PIDFLOCK, and trace_with_mask.

00032                : 
00033     m_fd (-1),
00034     m_error (0),
00035     m_error_msg ("no errors")
00036 {
00037     trace_with_mask ("PidFileLock::PidFileLock", PIDFLOCK);
00038 
00039     l_whence = SEEK_SET;
00040     l_start = l_len = l_pid = 0;
00041 }

PidFileLock::~PidFileLock (  ) 

Destructor.

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

Definition at line 44 of file PidFileLock.cpp.

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

00045 {
00046     trace_with_mask ("PidFileLock::~PidFileLock", PIDFLOCK);
00047 
00048     if (m_fd != -1) {
00049         if (unlock_region () == 0) {    // if we had a lock
00050             DL((PIDFLOCK,"PID file unlocked.\n"));
00051 
00052             unlink (m_filename.c_str ());
00053             DL((PIDFLOCK,"PID file removed.\n"));
00054         }
00055         close (m_fd);
00056         DL((PIDFLOCK,"PID lock file closed.\n"));
00057     }
00058 }


Member Function Documentation

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 62 of file PidFileLock.cpp.

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().

00063 {
00064     trace_with_mask ("PidFileLock::lock", PIDFLOCK);
00065 
00066 #if defined(WIN32)
00067     return true;
00068 #else
00069     int val;
00070     int len;
00071     m_filename = Utils::strenv (fname_.c_str ());
00072     val = len = 0;
00073 
00074     DL((PIDFLOCK,"PID lock file: \"%s\"\n", m_filename.c_str ()));
00075     
00076     if (open_pid_file (m_filename) < 0) {
00077         goto done;
00078     }
00079     DL((PIDFLOCK,"PID lock file opened and locked (fd=%d).\n", m_fd));
00080 
00083     if (ftruncate (m_fd, 0) < 0) {
00084         log_error("ftruncate() error");
00085         goto done;
00086     }
00087     DL((PIDFLOCK,"PID lock file truncated.\n"));
00088 
00091     if (write_pid () < 0) {
00092         log_error("write(PID) error");
00093         goto done;
00094     }
00095 
00098     if ((val = ::fcntl(m_fd, F_GETFD, 0)) < 0) {
00099         log_error("fcntl(F_GETFD) error");
00100         goto done;
00101     }
00102     val |= FD_CLOEXEC;
00103     
00104     if (::fcntl (m_fd, F_SETFD, val) < 0) {
00105         log_error("fcntl(F_SETFD) error");
00106         goto done;
00107     }
00108     DL((PIDFLOCK,"CLOSE-ON-EXEC is set on FD.\n"));
00109 
00110  done:
00111     if (get_error () != 0) {
00112         ::close (m_fd);
00113         m_fd = -1;
00114     }
00115     return m_error == 0 ? true : false;
00116 
00117 #endif  // !def WIN32
00118 }

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

Return last errno value.

Returns:
0 for success, errno on error

Definition at line 134 of file PidFileLock.h.

References m_error.

Referenced by dump(), and lock().

00135 { 
00136     return m_error; 
00137 }

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

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

Definition at line 141 of file PidFileLock.h.

References m_error_msg.

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

00142 { 
00143     return m_error_msg.c_str (); 
00144 }

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 384 of file PidFileLock.cpp.

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

00385 {
00386     trace_with_mask("PidFileLock::dump", PIDFLOCK);
00387 
00388 #if !defined (WIN32)
00389 
00390     DL((PIDFLOCK,"m_filename : \"%s\"\n", m_filename.c_str()));
00391     DL((PIDFLOCK,"m_error    : %d\n",     get_error ()));
00392     DL((PIDFLOCK,"m_error_msg: \"%s\"\n", get_error_msg ()));
00393     DL((PIDFLOCK,"m_fd       : %d\n",     m_fd));
00394 
00395     if (m_fd == -1) return;
00396 
00397     test_region ();
00398 
00399     if (this->l_type == F_RDLCK)
00400         DL((PIDFLOCK,"l_type    : F_RDLCK"));
00401 
00402     if (this->l_type == F_WRLCK)
00403         DL((PIDFLOCK,"l_type    : F_WRLCK"));
00404 
00405     if (this->l_type == F_UNLCK)
00406         DL((PIDFLOCK,"l_type    : F_UNLCK"));
00407 
00408     DL((PIDFLOCK,"l_whence  : %s\n",
00409         this->l_whence == SEEK_SET ? "SEEK_SET" :
00410         this->l_whence == SEEK_CUR ? "SEEK_CUR" : "SEEK_END"));
00411 
00412     DL((PIDFLOCK,"l_start   : %d\n",   this->l_start));
00413     DL((PIDFLOCK,"l_len     : %d\n",   this->l_len  ));
00414     DL((PIDFLOCK,"l_pid     : %ld\n",  this->l_pid  ));
00415 
00416 #endif  // !def WIN32
00417 }

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 435 of file PidFileLock.cpp.

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

Referenced by lock().

00436 {
00437     trace_with_mask("PidFileLock::open_pid_file", PIDFLOCK);
00438 
00439 #if !defined (WIN32)
00440 
00441     m_fd = ::open (fname_.c_str (), O_WRONLY|O_CREAT, 0644);
00442     if (m_fd < 0) {
00443         log_error("open() error.");
00444         return -1;
00445     }
00446 
00451     pid_t owner_pid;
00452     if ((owner_pid = test_region ()) > 0) {
00453         log_error ("PID file is already locked (by someone).");
00454         m_error = EPERM;
00455         return -1;
00456     }
00457 
00460     if (lock_region () < 0) {
00461         if (errno == EACCES || errno == EAGAIN) {
00462             log_error("PID file is locked by another process");
00463         }
00464         else {
00465             log_error("write lock error");
00466         }
00467         return -1;
00468     }
00469 
00470 #endif  // !def WIN32
00471 
00472     return 0;
00473 }

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 178 of file PidFileLock.cpp.

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

Referenced by open_pid_file(), and write_pid().

00179 {
00180     trace_with_mask ("PidFileLock::lock_region", PIDFLOCK);
00181     int ret;
00182 
00183 #if defined (WIN32)
00184     return 0;
00185 #else
00186 
00187 #ifdef __CYGWIN__
00188     this->l_type   = F_RDLCK;   // shared lock
00189 #else
00190     this->l_type   = F_WRLCK;
00191 #endif
00192 
00193     this->l_start  = 0;
00194     this->l_whence = SEEK_SET;
00195     this->l_len    = 0;
00196 
00197     ret = ::fcntl (m_fd, F_SETLK, static_cast<struct flock*>(this));
00198 
00199     DL((PIDFLOCK,"fcntl(fd=%d, F_SETLK, %s) returned: %d\n", 
00200         m_fd, 
00201         (this->l_type == F_RDLCK ? "F_RDLCK" : "F_WRLCK"),
00202         ret));
00203 
00204     return (ret);
00205 
00206 #endif  // !def WIN32
00207 }

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 212 of file PidFileLock.cpp.

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

Referenced by get_lock_status(), and write_pid().

00213 {
00214     trace_with_mask ("PidFileLock::lock_region_exclusive", PIDFLOCK);
00215     int ret = 0;
00216 
00217 #if defined (WIN32)
00218     return 0;
00219 #else
00220 
00221 #ifdef __CYGWIN__
00222     this->l_type   = F_WRLCK;   // exclusive lock - read would fail
00223     this->l_start  = 0;
00224     this->l_whence = SEEK_SET;
00225     this->l_len    = 0;
00226 
00227     ret = ::fcntl (m_fd, F_SETLK, static_cast<struct flock*>(this));
00228 
00229     DL((PIDFLOCK,"fcntl(fd=%d, F_SETLK, F_WRLCK) returned: %d\n", m_fd, ret));
00230 #endif
00231 
00232     return (ret);
00233 
00234 #endif  // !def WIN32
00235 }

int PidFileLock::unlock_region (  )  [private]

Unlock the entire file.

Returns:
-1 on error; 0 on success.

Definition at line 239 of file PidFileLock.cpp.

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

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

00240 {
00241     trace_with_mask ("PidFileLock::unlock_region", PIDFLOCK);
00242     int ret;
00243 
00244 #if defined (WIN32)
00245     return 0;
00246 #else
00247 
00248     this->l_type   = F_UNLCK;
00249     this->l_start  = 0;
00250     this->l_whence = SEEK_SET;
00251     this->l_len    = 0;
00252 
00253     ret = ::fcntl (m_fd, F_SETLK, static_cast<struct flock*>(this));
00254 
00255     DL((PIDFLOCK,"fcntl(fd=%d, F_SETLK, F_UNLCK) returned: %d\n", 
00256         m_fd, ret));
00257 
00258     return (ret);
00259 
00260 #endif  // !def WIN32
00261 }

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 294 of file PidFileLock.cpp.

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

Referenced by test_region().

00295 {
00296     trace_with_mask ("PidFileLock::get_lock_status", PIDFLOCK);
00297     int ret;
00298 
00299 #if defined (WIN32)
00300     return 0;
00301 #else
00302 
00303 #ifndef __CYGWIN__              // POSIX-compliant locking
00304 
00305     this->l_type   = F_WRLCK;
00306     this->l_start  = 0;
00307     this->l_whence = SEEK_SET;
00308     this->l_len    = 0;
00309 
00310     ret = ::fcntl (m_fd, F_GETLK, static_cast<struct flock*>(this));
00311 
00312     DL((PIDFLOCK,"fcntl(fd=%d, F_GETLK, %s) returned: %d\n", 
00313         m_fd, 
00314         (this->l_type == F_RDLCK ? "F_RDLCK" : "F_WRLCK"),
00315         ret));
00316     if (ret < 0) {
00317         EL ((PIDFLOCK,"fcntl() failed. l_pid = %d\n", this->l_pid));
00318     }
00319     return (ret);
00320 
00321 #else  // CYGWIN
00322 
00323     if (lock_region_exclusive () < 0) {             // why exclusive?
00324         if (unlock_region () < 0) {                 // already locked 
00325             char buf[64];
00326             pid_t pid;                              // someone else got it
00327             this->l_type = F_RDLCK;
00328             if (read (m_fd, buf, 64) > 0) {
00329                 if (sscanf (buf, "%d", &pid) == 1) {
00330                     this->l_pid = pid;
00331                 }
00332             }
00333             else {
00334                 this->l_pid = 1;                    // no real PID information
00335             }
00336         }
00337     }
00338     else {
00339         unlock_region ();           // return the lock into its prestine state
00340     }
00341     return (0);
00342     
00343 #endif  // !def CYGWIN
00344 
00345 #endif  // !def WIN32
00346 
00347 }

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 132 of file PidFileLock.cpp.

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

Referenced by lock().

00133 {
00134     trace_with_mask ("PidFileLock::write_pid", PIDFLOCK);
00135 
00136 #if defined (WIN32)
00137     return 0;
00138 #else
00139     std::ostringstream mypid;
00140     size_t len;
00141 
00142     this->l_pid = getpid ();
00143     mypid << this->l_pid << std::ends;
00144     len = strlen (mypid.str ().c_str ());
00145     
00146 #ifdef __CYGWIN__
00147 
00148     unlock_region ();           // remove shared (weak) lock
00149     lock_region_exclusive ();   
00150 
00151     if (write (m_fd, mypid.str ().c_str (), len) != len) {
00152         return -1;
00153     }
00154     DL((PIDFLOCK,"Wrote PID=%d to the lock file.\n", l_pid));
00155     unlock_region ();           // give up the exclusive lock
00156     lock_region ();             // place shared (weak) lock 
00157 
00158 #else  // POSIX-compliant locks
00159 
00160     if (write (m_fd, mypid.str ().c_str (), len) != len) {
00161         return -1;
00162     }
00163     DL((PIDFLOCK,"Wrote PID=%d to the lock file.\n", this->l_pid));
00164 
00165 #endif
00166     return 0;
00167 
00168 #endif  // !def WIN32
00169 }

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 355 of file PidFileLock.cpp.

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

Referenced by dump(), and open_pid_file().

00356 {
00357     trace_with_mask ("PidFileLock::test_region", PIDFLOCK);
00358     int ret;
00359 
00360 #if defined (WIN32)
00361     return 0;
00362 #else
00363 
00364     ret = get_lock_status ();
00365 
00366     if (ret < 0) {
00367         DL((PIDFLOCK,"Failed to retrieve lock status.\n"));
00368         return 1;
00369     }
00370     if (this->l_type == F_UNLCK) {
00371         DL((PIDFLOCK,"Region is not locked.\n"));
00372         return(0);  
00373     }
00374 
00375     DL((PIDFLOCK,"Region is already locked by PID %d\n", this->l_pid));
00376     return (this->l_pid);
00377 
00378 #endif  // !def WIN32
00379 }

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 421 of file PidFileLock.cpp.

References ASSA::ASSAERR, EL, and m_error.

Referenced by lock(), and open_pid_file().

00422 {
00423     m_error = errno;
00424     EL((ASSAERR, 
00425         "Error: \"Failed to get a lock on PID file - %s\".\n", msg_));
00426 }


Member Data Documentation

Lock file name.

Definition at line 120 of file PidFileLock.h.

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

int ASSA::PidFileLock::m_fd [private]

Last system call error.

Definition at line 126 of file PidFileLock.h.

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

Error explanation.

Definition at line 129 of file PidFileLock.h.

Referenced by get_error_msg().


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

Generated on Sun Mar 2 14:08:17 2008 for libassa by  doxygen 1.5.5