/home/vlg/develop/libASSA/libassa/assa/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 #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 }
00119 
00120 
00130 int
00131 PidFileLock::
00132 write_pid ()
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 }
00170 
00171 
00172 //------------------------------------------------------------------------------
00173 //   Utility functions
00174 //------------------------------------------------------------------------------
00175 
00176 int
00177 PidFileLock::
00178 lock_region ()
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 }
00208 
00209 
00210 int
00211 PidFileLock::
00212 lock_region_exclusive ()
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 }
00236 
00237 int
00238 PidFileLock::
00239 unlock_region ()
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 }
00262 
00263 
00292 int
00293 PidFileLock::
00294 get_lock_status ()
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 }
00348 
00353 pid_t
00354 PidFileLock::
00355 test_region ()
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 }
00380 
00381 
00382 void
00383 PidFileLock::
00384 dump (void) 
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 }
00418 
00419 void
00420 PidFileLock::
00421 log_error (const char* msg_)
00422 {
00423     m_error = errno;
00424     EL((ASSAERR, 
00425         "Error: \"Failed to get a lock on PID file - %s\".\n", msg_));
00426 }
00427 
00428 
00433 pid_t
00434 PidFileLock::
00435 open_pid_file (const std::string& fname_)
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 }
00474 

Generated on Sun Aug 13 15:08:00 2006 for libassa by  doxygen 1.4.6