00001
00002
00003
00004
00005
00006
00007
00008
00009 static const char rcsid[] __attribute__((used)) = "$Id: LockFile.cpp 1230 2014-11-16 02:32:01Z sella $";
00010
00011 #include "LockFile.h"
00012 #include "CommonMacro.h"
00013 #include "String.h"
00014 #include "File.h"
00015
00016 #include <sys/file.h>
00017
00018 using namespace sella::util;
00019
00020 LockFile::LockFile(const std::string &fullpath, useconds_t stale, bool removeStale, uid_t uid, gid_t gid, int mode) throw () :
00021 fullpath(fullpath),
00022 stale(stale),
00023 removeStale(removeStale),
00024 uid(uid),
00025 gid(gid),
00026 mode(mode),
00027 fd(0)
00028 { }
00029
00030 LockFile::~LockFile() {
00031 try {
00032 unlock();
00033 } catch (...) {
00034 close();
00035 }
00036 }
00037
00038 void LockFile::setStaleAge(useconds_t stale) throw () {
00039 if (stale > 0) {
00040 this->stale = stale;
00041 } else {
00042 this->stale = -1;
00043 }
00044 }
00045
00046 void LockFile::setUID(uid_t uid) throw () {
00047 if (uid > (__UID_T_TYPE) -1) {
00048 this->uid = uid;
00049 } else {
00050 this->uid = (__UID_T_TYPE) -1;
00051 }
00052 }
00053
00054 void LockFile::setGID(gid_t gid) throw () {
00055 if (gid > (__GID_T_TYPE) -1) {
00056 this->gid = gid;
00057 } else {
00058 this->gid = (__GID_T_TYPE) -1;
00059 }
00060 }
00061
00062 void LockFile::setMode(int mode) throw () {
00063 if (mode > -1 && mode < 7777) {
00064 this->mode = mode;
00065 } else {
00066 this->mode = 0644;
00067 }
00068 }
00069
00070 bool LockFile::lock(useconds_t interval) throw (FileException) {
00071 return tryLockCount(-1, interval);
00072 }
00073
00074 bool LockFile::tryLock(void) throw (FileException) {
00075 std::lock_guard<std::mutex> lg(mutex);
00076
00077 return lock_nonblock();
00078 }
00079
00080 bool LockFile::tryLockUntil(time_t timeout, useconds_t interval) throw (FileException) {
00081 time_t now = time(NULL);
00082 std::lock_guard<std::mutex> lg(mutex);
00083
00084 do {
00085 if (lock_nonblock()) {
00086 return true;
00087 }
00088
00089 usleep(interval);
00090 } while (now < timeout);
00091
00092 return false;
00093 }
00094
00095 bool LockFile::tryLockFor(time_t duration, useconds_t interval) throw (FileException) {
00096 time_t now = time(NULL);
00097 time_t timeout = now + duration;
00098 std::lock_guard<std::mutex> lg(mutex);
00099
00100 do {
00101 if (lock_nonblock()) {
00102 return true;
00103 }
00104
00105 usleep(interval);
00106 } while (now < timeout);
00107
00108 return false;
00109 }
00110
00111 bool LockFile::tryLockCount(int64_t retries, useconds_t interval) throw (FileException) {
00112 int64_t r = 0;
00113 std::lock_guard<std::mutex> lg(mutex);
00114
00115 do {
00116 if (lock_nonblock()) {
00117 return true;
00118 }
00119
00120 usleep(interval);
00121 } while (fd < 1 && (retries < 1 || r++ < retries));
00122
00123 return false;
00124
00125 }
00126
00127 bool LockFile::unlock(void) throw (FileException) {
00128 std::lock_guard<std::mutex> lg(mutex);
00129
00130 if (fd > 0) {
00131 if (!unlink()) {
00132 close();
00133
00134 THROW2(FileException, "Failed to remove lockfile '%s'", fullpath.c_str());
00135 }
00136
00137 close();
00138
00139 return true;
00140 }
00141
00142 return false;
00143 }
00144
00145 bool LockFile::remove(void) throw () {
00146 std::lock_guard<std::mutex> lg(mutex);
00147
00148 if (fd > 0) {
00149 return false;
00150 }
00151
00152 return unlink();
00153 }
00154
00155 bool LockFile::ownLock(void) throw () {
00156 return (fd > 0);
00157 }
00158
00159 bool LockFile::isLocked(void) throw () {
00160 if (ownLock()) {
00161 return true;
00162 }
00163
00164 return File::exists(fullpath, true);
00165 }
00166
00167 bool LockFile::isStale(void) throw () {
00168 if (stale > 0) {
00169 try {
00170 return (time(NULL) - File::getCTime(fullpath, true) > stale);
00171 } catch (...) { }
00172 }
00173
00174 return false;
00175 }
00176
00177 bool LockFile::lock_nonblock() throw (FileException) {
00178 int pid;
00179 struct stat st0, st1;
00180
00181 if (removeStale && isStale()) {
00182 unlink();
00183 close();
00184 }
00185
00186 if (isLocked()) {
00187 return false;
00188 }
00189
00190 while (1) {
00191 if ((fd = open(fullpath.c_str(), O_WRONLY|O_CREAT|O_DSYNC, mode)) < 0) {
00192 THROW2(FileException, "Failed to open or create lockfile '%s'", fullpath.c_str());
00193 }
00194
00195 if (flock(fd, LOCK_EX|LOCK_NB) != 0) {
00196 close();
00197
00198 if (errno == EWOULDBLOCK) {
00199 return false;
00200 }
00201
00202 THROW2(FileException, "Failed to lock '%s'", fullpath.c_str());
00203 }
00204
00205 if (fstat(fd, &st0) < 0 || stat(fullpath.c_str(), &st1) < 0) {
00206 close();
00207 } else if (st0.st_ino == st1.st_ino) {
00208 break;
00209 }
00210 }
00211
00212 pid = getpid();
00213
00214 char buf[16] = { 0 };
00215 snprintf(buf, sizeof(buf), "%d\n", pid);
00216
00217 if (write(fd, buf, strlen(buf)) < 1) {
00218 unlink();
00219 close();
00220
00221 THROW2(FileException, "Failed to write pid %d to lockfile '%s'", pid, fullpath.c_str());
00222 }
00223
00224 if (uid != (__UID_T_TYPE) -1 || gid != (__GID_T_TYPE) -1) {
00225 if (fchown(fd, uid, gid) != 0) {
00226 unlink();
00227 close();
00228
00229 THROW2(FileException, "Failed to change owner of lockfile '%s' to %d:%d", fullpath.c_str(), uid, gid);
00230 }
00231 }
00232
00233 return true;
00234 }
00235
00236 bool LockFile::unlink(void) throw () {
00237 int save = errno;
00238 bool res = (::unlink(fullpath.c_str()) == 0);
00239
00240 errno = save;
00241
00242 return res;
00243 }
00244
00245 void LockFile::close(void) throw () {
00246 int save = errno;
00247
00248 if (fd > 0) {
00249 ::close(fd);
00250 fd = 0;
00251 }
00252
00253 errno = save;
00254 }
00255
00256
00257
00258