1 // -*- C++ -*-
2 //
3 // Copyright (C) 2012-2013, Vaclav Zeman. All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modifica-
6 // tion, are permitted provided that the following conditions are met:
7 //
8 // 1. Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 //
11 // 2. Redistributions in binary form must reproduce the above copyright notice,
12 // this list of conditions and the following disclaimer in the documentation
13 // and/or other materials provided with the distribution.
14 //
15 // THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
16 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
18 // APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
19 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
20 // DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
21 // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
22 // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26 #include <log4cplus/config.hxx>
27
28 #if defined (LOG4CPLUS_HAVE_SYS_TYPES_H)
29 #include <sys/types.h>
30 #endif
31 #if defined (LOG4CPLUS_HAVE_SYS_STAT_H)
32 #include <sys/stat.h>
33 #endif
34 #if defined (LOG4CPLUS_HAVE_SYS_FILE_H)
35 #include <sys/file.h>
36 #endif
37 #if defined (LOG4CPLUS_HAVE_SYS_LOCKING_H)
38 #include <sys/locking.h>
39 #endif
40 #if defined (LOG4CPLUS_HAVE_UNISTD_H)
41 #include <unistd.h>
42 #endif
43 #if defined (LOG4CPLUS_HAVE_FCNTL_H)
44 #include <fcntl.h>
45 #endif
46 #if defined (LOG4CPLUS_HAVE_IO_H)
47 #include <io.h>
48 #endif
49 #if defined (_WIN32)
50 #include <tchar.h>
51 #include <share.h>
52 #endif
53 #include <log4cplus/config/windowsh-inc.h>
54
55 #include <stdexcept>
56 #include <cerrno>
57 #include <limits>
58 #include <cstring>
59
60 #include <log4cplus/helpers/lockfile.h>
61 #include <log4cplus/helpers/stringhelper.h>
62 #include <log4cplus/helpers/loglog.h>
63
64 #if defined (_WIN32)
65 # if _WIN32_WINNT < 0x0501
66 # define LOG4CPLUS_USE_WIN32_LOCKING
67 # else
68 # define LOG4CPLUS_USE_WIN32_LOCKFILEEX
69 # endif
70 #else
71 # if defined (O_EXLOCK)
72 # define LOG4CPLUS_USE_O_EXLOCK
73 # elif defined (LOG4CPLUS_HAVE_FCNTL) && defined (F_SETLKW)
74 # define LOG4CPLUS_USE_SETLKW
75 # elif defined (LOG4CPLUS_HAVE_LOCKF)
76 # define LOG4CPLUS_USE_LOCKF
77 # elif defined (LOG4CPLUS_HAVE_FLOCK)
78 # define LOG4CPLUS_USE_FLOCK
79 # endif
80 # if defined (LOG4CPLUS_USE_O_EXLOCK) || defined (LOG4CPLUS_USE_SETLKW) \
81 || defined (LOG4CPLUS_USE_LOCKF) || defined (LOG4CPLUS_USE_FLOCK)
82 # define LOG4CPLUS_USE_POSIX_LOCKING
83 # endif
84 #endif
85
86 #if ! defined (LOG4CPLUS_USE_POSIX_LOCKING) && ! defined (_WIN32)
87 #error "no usable file locking"
88 #endif
89
90 namespace log4cplus { namespace helpers {
91
92
93 #if defined (_WIN32)
94 #if defined (__BORLANDC__)
95 int const OPEN_FLAGS = O_RDWR | O_CREAT | O_NOINHERIT;
96 int const OPEN_SHFLAGS = SH_DENYNO;
97 int const OPEN_MODE = S_IREAD | S_IWRITE;
98 #else
99 int const OPEN_FLAGS = _O_RDWR | _O_CREAT /*| _O_TEMPORARY*/ | _O_NOINHERIT;
100 int const OPEN_SHFLAGS = _SH_DENYNO;
101 int const OPEN_MODE = _S_IREAD | _S_IWRITE;
102 #endif
103
104 namespace
105 {
106
107 static
108 HANDLE
get_os_HANDLE(int fd,helpers::LogLog & loglog=helpers::getLogLog ())109 get_os_HANDLE (int fd, helpers::LogLog & loglog = helpers::getLogLog ())
110 {
111 HANDLE fh = reinterpret_cast<HANDLE>(_get_osfhandle (fd));
112 if (fh == INVALID_HANDLE_VALUE)
113 loglog.error (tstring (LOG4CPLUS_TEXT ("_get_osfhandle() failed: "))
114 + convertIntegerToString (errno), true);
115
116 return fh;
117 }
118
119 } // namespace
120
121 #elif defined (LOG4CPLUS_USE_POSIX_LOCKING)
122 int const OPEN_FLAGS = O_RDWR | O_CREAT
123 #if defined (O_CLOEXEC)
124 | O_CLOEXEC
125 #endif
126 ;
127
128 mode_t const OPEN_MODE = (S_IRWXU ^ S_IXUSR)
129 | (S_IRWXG ^ S_IXGRP)
130 | (S_IRWXO ^ S_IXOTH);
131
132 #endif
133
134
135 //! Helper function that sets FD_CLOEXEC on descriptor on platforms
136 //! that support it.
137 LOG4CPLUS_PRIVATE
138 bool
trySetCloseOnExec(int fd,helpers::LogLog & loglog=helpers::getLogLog ())139 trySetCloseOnExec (int fd, helpers::LogLog & loglog = helpers::getLogLog ())
140 {
141 #if defined (WIN32)
142 int ret = SetHandleInformation (get_os_HANDLE (fd), HANDLE_FLAG_INHERIT, 0);
143 if (! ret)
144 {
145 DWORD eno = GetLastError ();
146 loglog.warn (
147 tstring (
148 LOG4CPLUS_TEXT ("could not unset HANDLE_FLAG_INHERIT on fd: "))
149 + convertIntegerToString (fd)
150 + LOG4CPLUS_TEXT (", errno: ")
151 + convertIntegerToString (eno));
152 return false;
153 }
154
155 #elif defined (FD_CLOEXEC)
156 int ret = fcntl (fd, F_SETFD, FD_CLOEXEC);
157 if (ret == -1)
158 {
159 int eno = errno;
160 loglog.warn (
161 tstring (LOG4CPLUS_TEXT ("could not set FD_CLOEXEC on fd: "))
162 + convertIntegerToString (fd)
163 + LOG4CPLUS_TEXT (", errno: ")
164 + convertIntegerToString (eno));
165 return false;
166 }
167 #else
168 return false;
169
170 #endif
171
172 return true;
173 }
174
175
176 //
177 //
178 //
179
180 struct LockFile::Impl
181 {
182 #if defined (LOG4CPLUS_USE_POSIX_LOCKING) \
183 || defined (_WIN32)
184 int fd;
185
186 #endif
187 };
188
189
190 //
191 //
192 //
193
LockFile(tstring const & lf)194 LockFile::LockFile (tstring const & lf)
195 : lock_file_name (lf)
196 , data (new LockFile::Impl)
197 {
198 #if defined (LOG4CPLUS_USE_O_EXLOCK)
199 data->fd = -1;
200
201 #else
202 open (OPEN_FLAGS);
203
204 #endif
205 }
206
207
~LockFile()208 LockFile::~LockFile ()
209 {
210 close ();
211 delete data;
212 }
213
214
215 void
open(int open_flags) const216 LockFile::open (int open_flags) const
217 {
218 LogLog & loglog = getLogLog ();
219
220 #if defined (_WIN32)
221 # if defined (LOG4CPLUS_HAVE__TSOPEN_S) && defined (_tsopen_s)
222 errno_t eno = _tsopen_s (&data->fd, lock_file_name.c_str (), open_flags,
223 OPEN_SHFLAGS, OPEN_MODE);
224 if (eno != 0)
225 # elif defined (LOG4CPLUS_HAVE__TSOPEN) && defined (_tsopen)
226 data->fd = _tsopen (lock_file_name.c_str (), open_flags, OPEN_SHFLAGS,
227 OPEN_MODE);
228 if (data->fd == -1)
229 # else
230 # error "Neither _tsopen_s() nor _tsopen() is available."
231 # endif
232 loglog.error (tstring (LOG4CPLUS_TEXT("could not open or create file "))
233 + lock_file_name, true);
234
235 #elif defined (LOG4CPLUS_USE_POSIX_LOCKING)
236 data->fd = ::open (LOG4CPLUS_TSTRING_TO_STRING (lock_file_name).c_str (),
237 open_flags, OPEN_MODE);
238 if (data->fd == -1)
239 loglog.error (
240 tstring (LOG4CPLUS_TEXT ("could not open or create file "))
241 + lock_file_name, true);
242
243 #if ! defined (O_CLOEXEC)
244 if (! trySetCloseOnExec (data->fd, loglog))
245 loglog.warn (
246 tstring (LOG4CPLUS_TEXT("could not set FD_CLOEXEC on file "))
247 + lock_file_name);
248
249 #endif
250 #endif
251 }
252
253
254 void
close() const255 LockFile::close () const
256 {
257 #if defined (_WIN32)
258 if (data->fd >= 0)
259 _close (data->fd);
260
261 data->fd = -1;
262
263 #elif defined (LOG4CPLUS_USE_POSIX_LOCKING)
264 if (data->fd >= 0)
265 ::close (data->fd);
266
267 data->fd = -1;
268
269 #endif
270 }
271
272
273 void
lock() const274 LockFile::lock () const
275 {
276 LogLog & loglog = getLogLog ();
277 int ret = 0;
278 (void) loglog;
279 (void) ret;
280
281 #if defined (LOG4CPLUS_USE_WIN32_LOCKFILEEX)
282 HANDLE fh = get_os_HANDLE (data->fd, loglog);
283
284 OVERLAPPED overlapped;
285 std::memset (&overlapped, 0, sizeof (overlapped));
286 overlapped.hEvent = 0;
287
288 ret = LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0,
289 (std::numeric_limits<DWORD>::max) (),
290 (std::numeric_limits<DWORD>::max) (), &overlapped);
291 if (! ret)
292 loglog.error (tstring (LOG4CPLUS_TEXT ("LockFileEx() failed: "))
293 + convertIntegerToString (GetLastError ()), true);
294
295 #elif defined (LOG4CPLUS_USE_WIN32_LOCKING)
296 ret = _locking (data->fd, _LK_LOCK, (std::numeric_limits<long>::max) ());
297 if (ret != 0)
298 loglog.error (tstring (LOG4CPLUS_TEXT ("_locking() failed: "))
299 + convertIntegerToString (errno), true);
300
301 #elif defined (LOG4CPLUS_USE_O_EXLOCK)
302 open (OPEN_FLAGS | O_EXLOCK);
303
304 #elif defined (LOG4CPLUS_USE_SETLKW)
305 do
306 {
307 struct flock fl;
308 fl.l_type = F_WRLCK;
309 fl.l_whence = SEEK_SET;
310 fl.l_start = 0;
311 fl.l_len = 0;
312 ret = fcntl (data->fd, F_SETLKW, &fl);
313 if (ret == -1 && errno != EINTR)
314 loglog.error (tstring (LOG4CPLUS_TEXT("fcntl(F_SETLKW) failed: "))
315 + convertIntegerToString (errno), true);
316 }
317 while (ret == -1);
318
319 #elif defined (LOG4CPLUS_USE_LOCKF)
320 do
321 {
322 ret = lockf (data->fd, F_LOCK, 0);
323 if (ret == -1 && errno != EINTR)
324 loglog.error (tstring (LOG4CPLUS_TEXT("lockf() failed: "))
325 + convertIntegerToString (errno), true);
326 }
327 while (ret == -1);
328
329 #elif defined (LOG4CPLUS_USE_FLOCK)
330 do
331 {
332 ret = flock (data->fd, LOCK_EX);
333 if (ret == -1 && errno != EINTR)
334 loglog.error (tstring (LOG4CPLUS_TEXT("flock() failed: "))
335 + convertIntegerToString (errno), true);
336 }
337 while (ret == -1);
338
339 #endif
340 }
341
342
unlock() const343 void LockFile::unlock () const
344 {
345 LogLog & loglog = getLogLog ();
346 int ret = 0;
347
348 #if defined (LOG4CPLUS_USE_WIN32_LOCKFILEEX)
349 HANDLE fh = get_os_HANDLE (data->fd, loglog);
350
351 ret = UnlockFile(fh, 0, 0, (std::numeric_limits<DWORD>::max) (),
352 (std::numeric_limits<DWORD>::max) ());
353 if (! ret)
354 loglog.error (tstring (LOG4CPLUS_TEXT ("UnlockFile() failed: "))
355 + convertIntegerToString (GetLastError ()), true);
356
357 #elif defined (LOG4CPLUS_USE_WIN32_LOCKING)
358 ret = _locking (data->fd, _LK_UNLCK, (std::numeric_limits<long>::max) ());
359 if (ret != 0)
360 loglog.error (tstring (LOG4CPLUS_TEXT ("_locking() failed: "))
361 + convertIntegerToString (errno), true);
362
363 #elif defined (LOG4CPLUS_USE_O_EXLOCK)
364 close ();
365
366 #elif defined (LOG4CPLUS_USE_SETLKW)
367 struct flock fl;
368 fl.l_type = F_UNLCK;
369 fl.l_whence = SEEK_SET;
370 fl.l_start = 0;
371 fl.l_len = 0;
372 ret = fcntl (data->fd, F_SETLKW, &fl);
373 if (ret != 0)
374 loglog.error (tstring (LOG4CPLUS_TEXT("fcntl(F_SETLKW) failed: "))
375 + convertIntegerToString (errno), true);
376
377 #elif defined (LOG4CPLUS_USE_LOCKF)
378 ret = lockf (data->fd, F_ULOCK, 0);
379 if (ret != 0)
380 loglog.error (tstring (LOG4CPLUS_TEXT("lockf() failed: "))
381 + convertIntegerToString (errno), true);
382
383 #elif defined (LOG4CPLUS_USE_FLOCK)
384 ret = flock (data->fd, LOCK_UN);
385 if (ret != 0)
386 loglog.error (tstring (LOG4CPLUS_TEXT("flock() failed: "))
387 + convertIntegerToString (errno), true);
388
389 #endif
390
391 }
392
393
394
395 } } // namespace log4cplus { namespace helpers {
396