PidFileLock.cpp

Go to the documentation of this file.
00001 // -*- c++ -*-
00002 //------------------------------------------------------------------------------
00003 //                            PidFileLock.cpp
00004 //------------------------------------------------------------------------------
00005 //  Copyright (c) 2001,2005 by Vladislav Grinchenko
00006 //
00007 //  This library is free software; you can redistribute it and/or
00008 //  modify it under the terms of the GNU Library General Public
00009 //  License as published by the Free Software Foundation; either
00010 //  version 2 of the License, or (at your option) any later version.
00011 //------------------------------------------------------------------------------
00012 
00013 //System Includes
00014 #include <errno.h>      // errno
00015 #include <string.h>     // strerror(3)
00016 #include <unistd.h>
00017 #include <fcntl.h>
00018 #include <sstream>      
00019 #include <stdio.h>
00020 
00021 //Local Includes
00022 #include "assa/CommonUtils.h"
00023 #include "assa/PidFileLock.h"
00024 
00025 using namespace ASSA;
00026 
00027 //------------------------------------------------------------------------------
00028 // External Events
00029 //------------------------------------------------------------------------------
00030 
00031 PidFileLock::
00032 PidFileLock () : 
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 }
00042 
00043 PidFileLock::
00044 ~PidFileLock ()
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 }
00059 
00060 bool
00061 PidFileLock::
00062 lock (const string& fname_)
00063 {
00064     trace_with_mask ("PidFileLock::lock", PIDFLOCK);
00065     
00066     int val;
00067     int len;
00068     m_filename = Utils::strenv (fname_.c_str ());
00069     val = len = 0;
00070 
00071     DL((PIDFLOCK,"PID lock file: \"%s\"\n", m_filename.c_str ()));
00072     
00073     if (open_pid_file (m_filename) < 0) {
00074         goto done;
00075     }
00076     DL((PIDFLOCK,"PID lock file opened and locked (fd=%d).\n", m_fd));
00077 
00080     if (ftruncate (m_fd, 0) < 0) {
00081         log_error("ftruncate() error");
00082         goto done;
00083     }
00084     DL((PIDFLOCK,"PID lock file truncated.\n"));
00085 
00088     if (write_pid () < 0) {
00089         log_error("write(PID) error");
00090         goto done;
00091     }
00092 
00095     if ((val = ::fcntl(m_fd, F_GETFD, 0)) < 0) {
00096         log_error("fcntl(F_GETFD) error");
00097         goto done;
00098     }
00099     val |= FD_CLOEXEC;
00100     
00101     if (::fcntl (m_fd, F_SETFD, val) < 0) {
00102         log_error("fcntl(F_SETFD) error");
00103         goto done;
00104     }
00105     DL((PIDFLOCK,"CLOSE-ON-EXEC is set on FD.\n"));
00106 
00107  done:
00108     if (get_error () != 0) {
00109         ::close (m_fd);
00110         m_fd = -1;
00111     }
00112     return m_error == 0 ? true : false;
00113 }
00114 
00115 
00125 int
00126 PidFileLock::
00127 write_pid ()
00128 {
00129     trace_with_mask ("PidFileLock::write_pid", PIDFLOCK);
00130 
00131     std::ostringstream mypid;
00132     size_t len;
00133 
00134     this->l_pid = getpid ();
00135     mypid << this->l_pid << std::ends;
00136     len = strlen (mypid.str ().c_str ());
00137     
00138 #ifdef __CYGWIN__
00139 
00140     unlock_region ();           // remove shared (weak) lock
00141     lock_region_exclusive ();   
00142 
00143     if (write (m_fd, mypid.str ().c_str (), len) != len) {
00144         return -1;
00145     }
00146     DL((PIDFLOCK,"Wrote PID=%d to the lock file.\n", l_pid));
00147     unlock_region ();           // give up the exclusive lock
00148     lock_region ();             // place shared (weak) lock 
00149 
00150 #else  // POSIX-compliant locks
00151 
00152     if (write (m_fd, mypid.str ().c_str (), len) != len) {
00153         return -1;
00154     }
00155     DL((PIDFLOCK,"Wrote PID=%d to the lock file.\n", this->l_pid));
00156 
00157 #endif
00158     return 0;
00159 }
00160 
00161 
00162 //------------------------------------------------------------------------------
00163 //   Utility functions
00164 //------------------------------------------------------------------------------
00165 
00166 int
00167 PidFileLock::
00168 lock_region ()
00169 {
00170     trace_with_mask ("PidFileLock::lock_region", PIDFLOCK);
00171     int ret;
00172 
00173 #ifdef __CYGWIN__
00174     this->l_type   = F_RDLCK;   // shared lock
00175 #else
00176     this->l_type   = F_WRLCK;
00177 #endif
00178 
00179     this->l_start  = 0;
00180     this->l_whence = SEEK_SET;
00181     this->l_len    = 0;
00182 
00183     ret = ::fcntl (m_fd, F_SETLK, static_cast<struct flock*>(this));
00184 
00185     DL((PIDFLOCK,"fcntl(fd=%d, F_SETLK, %s) returned: %d\n", 
00186         m_fd, 
00187         (this->l_type == F_RDLCK ? "F_RDLCK" : "F_WRLCK"),
00188         ret));
00189 
00190     return (ret);
00191 }
00192 
00193 
00194 int
00195 PidFileLock::
00196 lock_region_exclusive ()
00197 {
00198     trace_with_mask ("PidFileLock::lock_region_exclusive", PIDFLOCK);
00199     int ret = 0;
00200 
00201 #ifdef __CYGWIN__
00202     this->l_type   = F_WRLCK;   // exclusive lock - read would fail
00203     this->l_start  = 0;
00204     this->l_whence = SEEK_SET;
00205     this->l_len    = 0;
00206 
00207     ret = ::fcntl (m_fd, F_SETLK, static_cast<struct flock*>(this));
00208 
00209     DL((PIDFLOCK,"fcntl(fd=%d, F_SETLK, F_WRLCK) returned: %d\n", m_fd, ret));
00210 #endif
00211 
00212     return (ret);
00213 }
00214 
00215 int
00216 PidFileLock::
00217 unlock_region ()
00218 {
00219     trace_with_mask ("PidFileLock::unlock_region", PIDFLOCK);
00220     int ret;
00221 
00222     this->l_type   = F_UNLCK;
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_UNLCK) returned: %d\n", 
00230         m_fd, ret));
00231 
00232     return (ret);
00233 }
00234 
00235 
00264 int
00265 PidFileLock::
00266 get_lock_status ()
00267 {
00268     trace_with_mask ("PidFileLock::get_lock_status", PIDFLOCK);
00269     int ret;
00270 
00271 #ifndef __CYGWIN__              // POSIX-compliant locking
00272 
00273     this->l_type   = F_WRLCK;
00274     this->l_start  = 0;
00275     this->l_whence = SEEK_SET;
00276     this->l_len    = 0;
00277 
00278     ret = ::fcntl (m_fd, F_GETLK, static_cast<struct flock*>(this));
00279 
00280     DL((PIDFLOCK,"fcntl(fd=%d, F_GETLK, %s) returned: %d\n", 
00281         m_fd, 
00282         (this->l_type == F_RDLCK ? "F_RDLCK" : "F_WRLCK"),
00283         ret));
00284     if (ret < 0) {
00285         EL ((PIDFLOCK,"fcntl() failed. l_pid = %d\n", this->l_pid));
00286     }
00287     return (ret);
00288 
00289 #else  // CYGWIN
00290 
00291     if (lock_region_exclusive () < 0) {             // why exclusive?
00292         if (unlock_region () < 0) {                 // already locked 
00293             char buf[64];
00294             pid_t pid;                              // someone else got it
00295             this->l_type = F_RDLCK;
00296             if (read (m_fd, buf, 64) > 0) {
00297                 if (sscanf (buf, "%d", &pid) == 1) {
00298                     this->l_pid = pid;
00299                 }
00300             }
00301             else {
00302                 this->l_pid = 1;                    // no real PID information
00303             }
00304         }
00305     }
00306     else {
00307         unlock_region ();           // return the lock into its prestine state
00308     }
00309     return (0);
00310     
00311 #endif 
00312 }
00313 
00318 pid_t
00319 PidFileLock::
00320 test_region ()
00321 {
00322     trace_with_mask ("PidFileLock::test_region", PIDFLOCK);
00323     int ret;
00324 
00325     ret = get_lock_status ();
00326 
00327     if (ret < 0) {
00328         DL((PIDFLOCK,"Failed to retrieve lock status.\n"));
00329         return 1;
00330     }
00331     if (this->l_type == F_UNLCK) {
00332         DL((PIDFLOCK,"Region is not locked.\n"));
00333         return(0);  
00334     }
00335 
00336     DL((PIDFLOCK,"Region is already locked by PID %d\n", this->l_pid));
00337     return (this->l_pid);
00338 }
00339 
00340 
00341 void
00342 PidFileLock::
00343 dump (void) 
00344 {
00345     trace_with_mask("PidFileLock::dump", PIDFLOCK);
00346 
00347     DL((PIDFLOCK,"m_filename : \"%s\"\n", m_filename.c_str()));
00348     DL((PIDFLOCK,"m_error    : %d\n",     get_error ()));
00349     DL((PIDFLOCK,"m_error_msg: \"%s\"\n", get_error_msg ()));
00350     DL((PIDFLOCK,"m_fd       : %d\n",     m_fd));
00351 
00352     if (m_fd == -1) return;
00353 
00354     test_region ();
00355 
00356     if (this->l_type == F_RDLCK)
00357         DL((PIDFLOCK,"l_type    : F_RDLCK"));
00358 
00359     if (this->l_type == F_WRLCK)
00360         DL((PIDFLOCK,"l_type    : F_WRLCK"));
00361 
00362     if (this->l_type == F_UNLCK)
00363         DL((PIDFLOCK,"l_type    : F_UNLCK"));
00364 
00365     DL((PIDFLOCK,"l_whence  : %s\n",
00366         this->l_whence == SEEK_SET ? "SEEK_SET" :
00367         this->l_whence == SEEK_CUR ? "SEEK_CUR" : "SEEK_END"));
00368 
00369     DL((PIDFLOCK,"l_start   : %d\n",   this->l_start));
00370     DL((PIDFLOCK,"l_len     : %d\n",   this->l_len  ));
00371     DL((PIDFLOCK,"l_pid     : %ld\n",  this->l_pid  ));
00372 }
00373 
00374 void
00375 PidFileLock::
00376 log_error (const char* msg_)
00377 {
00378     m_error = errno;
00379     EL((ERROR, "Error: \"Failed to get a lock on PID file - %s\".\n", msg_));
00380 }
00381 
00382 
00387 pid_t
00388 PidFileLock::
00389 open_pid_file (const std::string& fname_)
00390 {
00391     trace_with_mask("PidFileLock::open_pid_file", PIDFLOCK);
00392 
00393     m_fd = ::open (fname_.c_str (), O_WRONLY|O_CREAT, 0644);
00394     if (m_fd < 0) {
00395         log_error("open() error.");
00396         return -1;
00397     }
00398 
00403     pid_t owner_pid;
00404     if ((owner_pid = test_region ()) > 0) {
00405         log_error ("PID file is already locked (by someone).");
00406         m_error = EPERM;
00407         return -1;
00408     }
00409 
00412     if (lock_region () < 0) {
00413         if (errno == EACCES || errno == EAGAIN) {
00414             log_error("PID file is locked by another process");
00415         }
00416         else {
00417             log_error("write lock error");
00418         }
00419         return -1;
00420     }
00421 
00422     return 0;
00423 }

Generated on Mon Dec 19 16:37:14 2005 for libassa by  doxygen 1.4.5