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