libassa  3.5.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
Public Member Functions | Private Member Functions | Private Attributes
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

Constructor.

Definition at line 32 of file PidFileLock.cpp.

References ASSA::PIDFLOCK, and trace_with_mask.

               : 
    m_fd (-1),
    m_error (0),
    m_error_msg ("no errors")
{
    trace_with_mask ("PidFileLock::PidFileLock", PIDFLOCK);

    l_whence = SEEK_SET;
    l_start = l_len = l_pid = 0;
}

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

{
    trace_with_mask ("PidFileLock::~PidFileLock", PIDFLOCK);

    if (m_fd != -1) {
        if (unlock_region () == 0) {    // if we had a lock
            DL((PIDFLOCK,"PID file unlocked.\n"));

            unlink (m_filename.c_str ());
            DL((PIDFLOCK,"PID file removed.\n"));
        }
        close (m_fd);
        DL((PIDFLOCK,"PID lock file closed.\n"));
    }
}

Member Function Documentation

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.

{
    trace_with_mask("PidFileLock::dump", PIDFLOCK);

#if !defined (WIN32)

    DL((PIDFLOCK,"m_filename : \"%s\"\n", m_filename.c_str()));
    DL((PIDFLOCK,"m_error    : %d\n",     get_error ()));
    DL((PIDFLOCK,"m_error_msg: \"%s\"\n", get_error_msg ()));
    DL((PIDFLOCK,"m_fd       : %d\n",     m_fd));

    if (m_fd == -1) return;

    test_region ();

    if (this->l_type == F_RDLCK)
        DL((PIDFLOCK,"l_type    : F_RDLCK"));

    if (this->l_type == F_WRLCK)
        DL((PIDFLOCK,"l_type    : F_WRLCK"));

    if (this->l_type == F_UNLCK)
        DL((PIDFLOCK,"l_type    : F_UNLCK"));

    DL((PIDFLOCK,"l_whence  : %s\n",
        this->l_whence == SEEK_SET ? "SEEK_SET" :
        this->l_whence == SEEK_CUR ? "SEEK_CUR" : "SEEK_END"));

    DL((PIDFLOCK,"l_start   : %d\n",   this->l_start));
    DL((PIDFLOCK,"l_len     : %d\n",   this->l_len  ));
    DL((PIDFLOCK,"l_pid     : %ld\n",  this->l_pid  ));

#endif  // !def WIN32
}
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().

{ 
    return m_error; 
}
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().

{ 
    return m_error_msg.c_str (); 
}
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().

{
    trace_with_mask ("PidFileLock::get_lock_status", PIDFLOCK);
    int ret;

#if defined (WIN32)
    return 0;
#else

#ifndef __CYGWIN__              // POSIX-compliant locking

    this->l_type   = F_WRLCK;
    this->l_start  = 0;
    this->l_whence = SEEK_SET;
    this->l_len    = 0;

    ret = ::fcntl (m_fd, F_GETLK, static_cast<struct flock*>(this));

    DL((PIDFLOCK,"fcntl(fd=%d, F_GETLK, %s) returned: %d\n", 
        m_fd, 
        (this->l_type == F_RDLCK ? "F_RDLCK" : "F_WRLCK"),
        ret));
    if (ret < 0) {
        EL ((PIDFLOCK,"fcntl() failed. l_pid = %d\n", this->l_pid));
    }
    return (ret);

#else  // CYGWIN

    if (lock_region_exclusive () < 0) {             // why exclusive?
        if (unlock_region () < 0) {                 // already locked 
            char buf[64];
            pid_t pid;                              // someone else got it
            this->l_type = F_RDLCK;
            if (read (m_fd, buf, 64) > 0) {
                if (sscanf (buf, "%d", &pid) == 1) {
                    this->l_pid = pid;
                }
            }
            else {
                this->l_pid = 1;                    // no real PID information
            }
        }
    }
    else {
        unlock_region ();           // return the lock into its prestine state
    }
    return (0);
    
#endif  // !def CYGWIN

#endif  // !def WIN32

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

{
    trace_with_mask ("PidFileLock::lock", PIDFLOCK);

#if defined(WIN32)
    return true;
#else
    int val;
    int len;
    m_filename = Utils::strenv (fname_.c_str ());
    val = len = 0;

    DL((PIDFLOCK,"PID lock file: \"%s\"\n", m_filename.c_str ()));
    
    if (open_pid_file (m_filename) < 0) {
        goto done;
    }
    DL((PIDFLOCK,"PID lock file opened and locked (fd=%d).\n", m_fd));

    if (ftruncate (m_fd, 0) < 0) {
        log_error("ftruncate() error");
        goto done;
    }
    DL((PIDFLOCK,"PID lock file truncated.\n"));

    if (write_pid () < 0) {
        log_error("write(PID) error");
        goto done;
    }

    if ((val = ::fcntl(m_fd, F_GETFD, 0)) < 0) {
        log_error("fcntl(F_GETFD) error");
        goto done;
    }
    val |= FD_CLOEXEC;
    
    if (::fcntl (m_fd, F_SETFD, val) < 0) {
        log_error("fcntl(F_SETFD) error");
        goto done;
    }
    DL((PIDFLOCK,"CLOSE-ON-EXEC is set on FD.\n"));

 done:
    if (get_error () != 0) {
        ::close (m_fd);
        m_fd = -1;
    }
    return m_error == 0 ? true : false;

#endif  // !def WIN32
}
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().

{
    trace_with_mask ("PidFileLock::lock_region", PIDFLOCK);
    int ret;

#if defined (WIN32)
    return 0;
#else

#ifdef __CYGWIN__
    this->l_type   = F_RDLCK;   // shared lock
#else
    this->l_type   = F_WRLCK;
#endif

    this->l_start  = 0;
    this->l_whence = SEEK_SET;
    this->l_len    = 0;

    ret = ::fcntl (m_fd, F_SETLK, static_cast<struct flock*>(this));

    DL((PIDFLOCK,"fcntl(fd=%d, F_SETLK, %s) returned: %d\n", 
        m_fd, 
        (this->l_type == F_RDLCK ? "F_RDLCK" : "F_WRLCK"),
        ret));

    return (ret);

#endif  // !def WIN32
}

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

{
    trace_with_mask ("PidFileLock::lock_region_exclusive", PIDFLOCK);
    int ret = 0;

#if defined (WIN32)
    return 0;
#else

#ifdef __CYGWIN__
    this->l_type   = F_WRLCK;   // exclusive lock - read would fail
    this->l_start  = 0;
    this->l_whence = SEEK_SET;
    this->l_len    = 0;

    ret = ::fcntl (m_fd, F_SETLK, static_cast<struct flock*>(this));

    DL((PIDFLOCK,"fcntl(fd=%d, F_SETLK, F_WRLCK) returned: %d\n", m_fd, ret));
#endif

    return (ret);

#endif  // !def WIN32
}
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().

{
    m_error = errno;
    EL((ASSAERR, 
        "Error: \"Failed to get a lock on PID file - %s\".\n", msg_));
}
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().

{
    trace_with_mask("PidFileLock::open_pid_file", PIDFLOCK);

#if !defined (WIN32)

    m_fd = ::open (fname_.c_str (), O_WRONLY|O_CREAT, 0644);
    if (m_fd < 0) {
        log_error("open() error.");
        return -1;
    }

    pid_t owner_pid;
    if ((owner_pid = test_region ()) > 0) {
        log_error ("PID file is already locked (by someone).");
        m_error = EPERM;
        return -1;
    }

    if (lock_region () < 0) {
        if (errno == EACCES || errno == EAGAIN) {
            log_error("PID file is locked by another process");
        }
        else {
            log_error("write lock error");
        }
        return -1;
    }

#endif  // !def WIN32

    return 0;
}
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().

{
    trace_with_mask ("PidFileLock::test_region", PIDFLOCK);
    int ret;

#if defined (WIN32)
    return 0;
#else

    ret = get_lock_status ();

    if (ret < 0) {
        DL((PIDFLOCK,"Failed to retrieve lock status.\n"));
        return 1;
    }
    if (this->l_type == F_UNLCK) {
        DL((PIDFLOCK,"Region is not locked.\n"));
        return(0);  
    }

    DL((PIDFLOCK,"Region is already locked by PID %d\n", this->l_pid));
    return (this->l_pid);

#endif  // !def WIN32
}
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().

{
    trace_with_mask ("PidFileLock::unlock_region", PIDFLOCK);
    int ret;

#if defined (WIN32)
    return 0;
#else

    this->l_type   = F_UNLCK;
    this->l_start  = 0;
    this->l_whence = SEEK_SET;
    this->l_len    = 0;

    ret = ::fcntl (m_fd, F_SETLK, static_cast<struct flock*>(this));

    DL((PIDFLOCK,"fcntl(fd=%d, F_SETLK, F_UNLCK) returned: %d\n", 
        m_fd, ret));

    return (ret);

#endif  // !def WIN32
}
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().

{
    trace_with_mask ("PidFileLock::write_pid", PIDFLOCK);

#if defined (WIN32)
    return 0;
#else
    std::ostringstream mypid;
    size_t len;

    this->l_pid = getpid ();
    mypid << this->l_pid << std::ends;
    len = strlen (mypid.str ().c_str ());
    
#ifdef __CYGWIN__

    unlock_region ();           // remove shared (weak) lock
    lock_region_exclusive ();   

    if (write (m_fd, mypid.str ().c_str (), len) != len) {
        return -1;
    }
    DL((PIDFLOCK,"Wrote PID=%d to the lock file.\n", l_pid));
    unlock_region ();           // give up the exclusive lock
    lock_region ();             // place shared (weak) lock 

#else  // POSIX-compliant locks

    if (write (m_fd, mypid.str ().c_str (), len) != len) {
        return -1;
    }
    DL((PIDFLOCK,"Wrote PID=%d to the lock file.\n", this->l_pid));

#endif
    return 0;

#endif  // !def WIN32
}

Member Data Documentation

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

int ASSA::PidFileLock::m_fd [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:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines