libassa 3.5.0
|
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