libassa  3.5.1
PidFileLock.cpp
Go to the documentation of this file.
1 // -*- c++ -*-
2 //------------------------------------------------------------------------------
3 // PidFileLock.cpp
4 //------------------------------------------------------------------------------
5 // Copyright (c) 2001,2005 by Vladislav Grinchenko
6 //
7 // This library is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU Library General Public
9 // License as published by the Free Software Foundation; either
10 // version 2 of the License, or (at your option) any later version.
11 //------------------------------------------------------------------------------
12 
13 //System Includes
14 #include <errno.h> // errno
15 #include <string.h> // strerror(3)
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <sstream>
19 #include <stdio.h>
20 
21 //Local Includes
22 #include "assa/CommonUtils.h"
23 #include "assa/PidFileLock.h"
24 
25 using namespace ASSA;
26 
27 //------------------------------------------------------------------------------
28 // External Events
29 //------------------------------------------------------------------------------
30 
32 PidFileLock () :
33  m_fd (-1),
34  m_error (0),
35  m_error_msg ("no errors")
36 {
37  trace_with_mask ("PidFileLock::PidFileLock", PIDFLOCK);
38 
39  l_whence = SEEK_SET;
40  l_start = l_len = l_pid = 0;
41 }
42 
44 ~PidFileLock ()
45 {
46  trace_with_mask ("PidFileLock::~PidFileLock", PIDFLOCK);
47 
48  if (m_fd != -1) {
49  if (unlock_region () == 0) { // if we had a lock
50  DL((PIDFLOCK,"PID file unlocked.\n"));
51 
52  unlink (m_filename.c_str ());
53  DL((PIDFLOCK,"PID file removed.\n"));
54  }
55  close (m_fd);
56  DL((PIDFLOCK,"PID lock file closed.\n"));
57  }
58 }
59 ␌
60 bool
62 lock (const string& fname_)
63 {
64  trace_with_mask ("PidFileLock::lock", PIDFLOCK);
65 
66 #if defined(WIN32)
67  return true;
68 #else
69  int val;
70  int len;
71  m_filename = Utils::strenv (fname_.c_str ());
72  val = len = 0;
73 
74  DL((PIDFLOCK,"PID lock file: \"%s\"\n", m_filename.c_str ()));
75 
76  if (open_pid_file (m_filename) < 0) {
77  goto done;
78  }
79  DL((PIDFLOCK,"PID lock file opened and locked (fd=%d).\n", m_fd));
80 
83  if (ftruncate (m_fd, 0) < 0) {
84  log_error("ftruncate() error");
85  goto done;
86  }
87  DL((PIDFLOCK,"PID lock file truncated.\n"));
88 
91  if (write_pid () < 0) {
92  log_error("write(PID) error");
93  goto done;
94  }
95 
98  if ((val = ::fcntl(m_fd, F_GETFD, 0)) < 0) {
99  log_error("fcntl(F_GETFD) error");
100  goto done;
101  }
102  val |= FD_CLOEXEC;
103 
104  if (::fcntl (m_fd, F_SETFD, val) < 0) {
105  log_error("fcntl(F_SETFD) error");
106  goto done;
107  }
108  DL((PIDFLOCK,"CLOSE-ON-EXEC is set on FD.\n"));
109 
110  done:
111  if (get_error () != 0) {
112  ::close (m_fd);
113  m_fd = -1;
114  }
115  return m_error == 0 ? true : false;
116 
117 #endif // !def WIN32
118 }
119 ␌
120 
130 int
132 write_pid ()
133 {
134  trace_with_mask ("PidFileLock::write_pid", PIDFLOCK);
135 
136 #if defined (WIN32)
137  return 0;
138 #else
139  std::ostringstream mypid;
140  size_t len;
141 
142  this->l_pid = getpid ();
143  mypid << this->l_pid << std::ends;
144  len = strlen (mypid.str ().c_str ());
145 
146 #ifdef __CYGWIN__
147 
148  unlock_region (); // remove shared (weak) lock
150 
151  if (write (m_fd, mypid.str ().c_str (), len) != len) {
152  return -1;
153  }
154  DL((PIDFLOCK,"Wrote PID=%d to the lock file.\n", l_pid));
155  unlock_region (); // give up the exclusive lock
156  lock_region (); // place shared (weak) lock
157 
158 #else // POSIX-compliant locks
159 
160  if (write (m_fd, mypid.str ().c_str (), len) != len) {
161  return -1;
162  }
163  DL((PIDFLOCK,"Wrote PID=%d to the lock file.\n", this->l_pid));
164 
165 #endif
166  return 0;
167 
168 #endif // !def WIN32
169 }
170 
171 ␌
172 //------------------------------------------------------------------------------
173 // Utility functions
174 //------------------------------------------------------------------------------
175 
176 int
178 lock_region ()
179 {
180  trace_with_mask ("PidFileLock::lock_region", PIDFLOCK);
181  int ret;
182 
183 #if defined (WIN32)
184  return 0;
185 #else
186 
187 #ifdef __CYGWIN__
188  this->l_type = F_RDLCK; // shared lock
189 #else
190  this->l_type = F_WRLCK;
191 #endif
192 
193  this->l_start = 0;
194  this->l_whence = SEEK_SET;
195  this->l_len = 0;
196 
197  ret = ::fcntl (m_fd, F_SETLK, static_cast<struct flock*>(this));
198 
199  DL((PIDFLOCK,"fcntl(fd=%d, F_SETLK, %s) returned: %d\n",
200  m_fd,
201  (this->l_type == F_RDLCK ? "F_RDLCK" : "F_WRLCK"),
202  ret));
203 
204  return (ret);
205 
206 #endif // !def WIN32
207 }
208 
209 
210 int
213 {
214  trace_with_mask ("PidFileLock::lock_region_exclusive", PIDFLOCK);
215  int ret = 0;
216 
217 #if defined (WIN32)
218  return 0;
219 #else
220 
221 #ifdef __CYGWIN__
222  this->l_type = F_WRLCK; // exclusive lock - read would fail
223  this->l_start = 0;
224  this->l_whence = SEEK_SET;
225  this->l_len = 0;
226 
227  ret = ::fcntl (m_fd, F_SETLK, static_cast<struct flock*>(this));
228 
229  DL((PIDFLOCK,"fcntl(fd=%d, F_SETLK, F_WRLCK) returned: %d\n", m_fd, ret));
230 #endif
231 
232  return (ret);
233 
234 #endif // !def WIN32
235 }
236 
237 int
239 unlock_region ()
240 {
241  trace_with_mask ("PidFileLock::unlock_region", PIDFLOCK);
242  int ret;
243 
244 #if defined (WIN32)
245  return 0;
246 #else
247 
248  this->l_type = F_UNLCK;
249  this->l_start = 0;
250  this->l_whence = SEEK_SET;
251  this->l_len = 0;
252 
253  ret = ::fcntl (m_fd, F_SETLK, static_cast<struct flock*>(this));
254 
255  DL((PIDFLOCK,"fcntl(fd=%d, F_SETLK, F_UNLCK) returned: %d\n",
256  m_fd, ret));
257 
258  return (ret);
259 
260 #endif // !def WIN32
261 }
262 
263 ␌
292 int
295 {
296  trace_with_mask ("PidFileLock::get_lock_status", PIDFLOCK);
297  int ret;
298 
299 #if defined (WIN32)
300  return 0;
301 #else
302 
303 #ifndef __CYGWIN__ // POSIX-compliant locking
304 
305  this->l_type = F_WRLCK;
306  this->l_start = 0;
307  this->l_whence = SEEK_SET;
308  this->l_len = 0;
309 
310  ret = ::fcntl (m_fd, F_GETLK, static_cast<struct flock*>(this));
311 
312  DL((PIDFLOCK,"fcntl(fd=%d, F_GETLK, %s) returned: %d\n",
313  m_fd,
314  (this->l_type == F_RDLCK ? "F_RDLCK" : "F_WRLCK"),
315  ret));
316  if (ret < 0) {
317  EL ((PIDFLOCK,"fcntl() failed. l_pid = %d\n", this->l_pid));
318  }
319  return (ret);
320 
321 #else // CYGWIN
322 
323  if (lock_region_exclusive () < 0) { // why exclusive?
324  if (unlock_region () < 0) { // already locked
325  char buf[64];
326  pid_t pid; // someone else got it
327  this->l_type = F_RDLCK;
328  if (read (m_fd, buf, 64) > 0) {
329  if (sscanf (buf, "%d", &pid) == 1) {
330  this->l_pid = pid;
331  }
332  }
333  else {
334  this->l_pid = 1; // no real PID information
335  }
336  }
337  }
338  else {
339  unlock_region (); // return the lock into its prestine state
340  }
341  return (0);
342 
343 #endif // !def CYGWIN
344 
345 #endif // !def WIN32
346 
347 }
348 ␌
353 pid_t
355 test_region ()
356 {
357  trace_with_mask ("PidFileLock::test_region", PIDFLOCK);
358  int ret;
359 
360 #if defined (WIN32)
361  return 0;
362 #else
363 
364  ret = get_lock_status ();
365 
366  if (ret < 0) {
367  DL((PIDFLOCK,"Failed to retrieve lock status.\n"));
368  return 1;
369  }
370  if (this->l_type == F_UNLCK) {
371  DL((PIDFLOCK,"Region is not locked.\n"));
372  return(0);
373  }
374 
375  DL((PIDFLOCK,"Region is already locked by PID %d\n", this->l_pid));
376  return (this->l_pid);
377 
378 #endif // !def WIN32
379 }
380 
381 ␌
382 void
384 dump (void)
385 {
386  trace_with_mask("PidFileLock::dump", PIDFLOCK);
387 
388 #if !defined (WIN32)
389 
390  DL((PIDFLOCK,"m_filename : \"%s\"\n", m_filename.c_str()));
391  DL((PIDFLOCK,"m_error : %d\n", get_error ()));
392  DL((PIDFLOCK,"m_error_msg: \"%s\"\n", get_error_msg ()));
393  DL((PIDFLOCK,"m_fd : %d\n", m_fd));
394 
395  if (m_fd == -1) return;
396 
397  test_region ();
398 
399  if (this->l_type == F_RDLCK)
400  DL((PIDFLOCK,"l_type : F_RDLCK"));
401 
402  if (this->l_type == F_WRLCK)
403  DL((PIDFLOCK,"l_type : F_WRLCK"));
404 
405  if (this->l_type == F_UNLCK)
406  DL((PIDFLOCK,"l_type : F_UNLCK"));
407 
408  DL((PIDFLOCK,"l_whence : %s\n",
409  this->l_whence == SEEK_SET ? "SEEK_SET" :
410  this->l_whence == SEEK_CUR ? "SEEK_CUR" : "SEEK_END"));
411 
412  DL((PIDFLOCK,"l_start : %d\n", this->l_start));
413  DL((PIDFLOCK,"l_len : %d\n", this->l_len ));
414  DL((PIDFLOCK,"l_pid : %ld\n", this->l_pid ));
415 
416 #endif // !def WIN32
417 }
418 
419 void
421 log_error (const char* msg_)
422 {
423  m_error = errno;
424  EL((ASSAERR,
425  "Error: \"Failed to get a lock on PID file - %s\".\n", msg_));
426 }
427 
428 ␌
433 pid_t
435 open_pid_file (const std::string& fname_)
436 {
437  trace_with_mask("PidFileLock::open_pid_file", PIDFLOCK);
438 
439 #if !defined (WIN32)
440 
441  m_fd = ::open (fname_.c_str (), O_WRONLY|O_CREAT, 0644);
442  if (m_fd < 0) {
443  log_error("open() error.");
444  return -1;
445  }
446 
451  pid_t owner_pid;
452  if ((owner_pid = test_region ()) > 0) {
453  log_error ("PID file is already locked (by someone).");
454  m_error = EPERM;
455  return -1;
456  }
457 
460  if (lock_region () < 0) {
461  if (errno == EACCES || errno == EAGAIN) {
462  log_error("PID file is locked by another process");
463  }
464  else {
465  log_error("write lock error");
466  }
467  return -1;
468  }
469 
470 #endif // !def WIN32
471 
472  return 0;
473 }
474 
#define EL(X)
A macro for writing error message to the Logger.
Definition: Logger.h:285
#define DL(X)
A macro for writing debug message to the Logger.
Definition: Logger.h:273
#define trace_with_mask(s, m)
trace_with_mask() is used to trace function call chain in C++ program.
Definition: Logger.h:437
A utility class for creating and managing process PID lock file.
int get_lock_status()
Retrieve lock status.
~PidFileLock()
Destructor.
Definition: PidFileLock.cpp:44
int write_pid()
Write our process pid to the lock file.
pid_t open_pid_file(const std::string &fname_)
Open pid file in a cross-platform way.
int m_error
Last system call error.
Definition: PidFileLock.h:126
const char * get_error_msg() const
In case of error, return a verbal description of the last error.
Definition: PidFileLock.h:141
PidFileLock()
Constructor.
Definition: PidFileLock.cpp:32
int get_error() const
Return last errno value.
Definition: PidFileLock.h:134
void log_error(const char *msg_)
Log an error message to the log file and set internal error to errno.
pid_t test_region()
Test if file is unlocked.
string m_filename
Lock file name.
Definition: PidFileLock.h:120
int m_fd
Lock file descriptor.
Definition: PidFileLock.h:123
void dump()
Write the state of the lock to debug file.
bool lock(const string &filename_)
Lock the file.
Definition: PidFileLock.cpp:62
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.
std::string strenv(const char *in_)
Expand the passed string in_ by substituting environment variable names for their values.
Definition: Acceptor.h:40
@ ASSAERR
ASSA and system errors
Definition: LogMask.h:34
@ PIDFLOCK
Class PidFileLock messages
Definition: LogMask.h:35
Socket & ends(Socket &os_)
ends manipulator.
Definition: Socket.h:622