Only use confstr on Linux to see if linuxthreads is in use.
[idzebra-moved-to-github.git] / util / flock.c
1 /* $Id: flock.c,v 1.15 2006-07-03 12:23:17 adam Exp $
2    Copyright (C) 1995-2006
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23
24 #include <stdio.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <sys/types.h>
30 #ifdef WIN32
31 #include <io.h>
32 #include <sys/locking.h>
33 #endif
34 #if HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37
38 #include <idzebra/flock.h>
39 #include <zebra-lock.h>
40 #include <yaz/xmalloc.h>
41 #include <yaz/log.h>
42
43 /** whether this module should debug */
44 #define DEBUG_FLOCK 1
45
46 /** have this module (mutex) been initialized? */
47 static int initialized = 0;
48
49 /** whether fcntl locks are shared for all threads in a process (POSIX) */
50 static int posix_locks = 1;
51
52 /** mutex for lock_list below */
53 Zebra_mutex lock_list_mutex;
54
55 /** our list of file locked files */
56 static struct zebra_lock_info *lock_list = 0;
57
58 /** the internal handle, with a pointer to each lock file info */
59 struct zebra_lock_handle {
60 #ifndef WIN32
61     /** so we can call zebra_lock_rdwr_wunlock or zebra_lock_lock_runlock */
62     int write_flag;
63 #endif
64     struct zebra_lock_info *p;
65 };
66
67 struct zebra_lock_info {
68     /** file descriptor */
69     int fd;
70     /** full path (xmalloc'ed) */
71     char *fname;
72     /** reference counter: number of zebra_lock_handles pointing to us */
73     int ref_count;
74 #ifndef WIN32
75     /** number of file write locks/read locks */
76     int no_file_write_lock;
77     int no_file_read_lock;
78     Zebra_lock_rdwr rdwr_lock;
79     Zebra_mutex file_mutex;
80 #endif
81     /** next in lock list */
82     struct zebra_lock_info *next;
83 };
84
85 static int log_level = 0;
86
87 char *zebra_mk_fname(const char *dir, const char *name)
88 {
89     int dlen = dir ? strlen(dir) : 0;
90     char *fname = xmalloc(dlen + strlen(name) + 3);
91     
92 #ifdef WIN32
93     if (dlen)
94     {
95         int last_one = dir[dlen-1];
96         
97         if (!strchr("/\\:", last_one))
98             sprintf(fname, "%s\\%s", dir, name);
99         else
100             sprintf(fname, "%s%s", dir, name);
101     }
102     else
103         sprintf(fname, "%s", name);
104 #else
105     if (dlen)
106     {
107         int last_one = dir[dlen-1];
108
109         if (!strchr("/", last_one))
110             sprintf(fname, "%s/%s", dir, name);
111         else
112             sprintf(fname, "%s%s", dir, name);
113     }
114     else
115         sprintf(fname, "%s", name);
116 #endif
117     return fname;
118 }
119
120 ZebraLockHandle zebra_lock_create(const char *dir, const char *name)
121 {
122     char *fname = zebra_mk_fname(dir, name);
123     struct zebra_lock_info *p = 0;
124     ZebraLockHandle h = 0;
125
126     assert(initialized);
127
128     zebra_mutex_lock(&lock_list_mutex);
129     /* see if we have the same filename in a global list of "lock files" */
130 #ifndef WIN32
131     if (posix_locks)
132     {
133         for (p = lock_list; p ; p = p->next)
134             if (!strcmp(p->fname, fname))
135                 break;
136     }
137 #endif
138     if (!p)
139     {   /* didn't match (or we didn't want it to match! */
140         p = (struct zebra_lock_info *) xmalloc(sizeof(*p));
141         
142         p->ref_count = 0;
143 #ifdef WIN32
144         p->fd = open(name, O_BINARY|O_RDONLY);
145         if (p->fd == -1)
146             p->fd = open(fname, (O_BINARY|O_CREAT|O_RDWR), 0666);
147 #else
148         p->fd = open(fname, (O_BINARY|O_CREAT|O_RDWR), 0666);
149 #endif
150         if (p->fd == -1)
151         {
152             xfree(p);
153             yaz_log(YLOG_WARN | YLOG_ERRNO, 
154                     "zebra_lock_create fail fname=%s", fname);
155             p = 0;
156         }
157         else
158         {
159             p->fname = fname;
160             fname = 0;  /* fname buffer now owned by p->fname */
161 #ifndef WIN32
162             if (posix_locks)
163                 zebra_lock_rdwr_init(&p->rdwr_lock);
164
165             zebra_mutex_init(&p->file_mutex);
166             p->no_file_write_lock = 0;
167             p->no_file_read_lock = 0;
168 #endif
169             p->next = lock_list;
170             lock_list = p;
171         }
172     }
173     if (p)
174     {
175         /* we have lock info so we can make a handle pointing to that */
176         p->ref_count++;
177         h = (ZebraLockHandle) xmalloc(sizeof(*h));
178         h->p = p;
179 #ifndef WIN32
180         h->write_flag = 0;
181 #endif
182         yaz_log(log_level, "zebra_lock_create fd=%d p=%p fname=%s",
183                 h->p->fd, h, p->fname);
184     }
185     zebra_mutex_unlock(&lock_list_mutex);
186     xfree(fname); /* free it - if it's still there */
187
188     return h;
189 }
190
191 void zebra_lock_destroy(ZebraLockHandle h)
192 {
193     if (!h)
194         return;
195     yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s",
196             h->p->fd, h, h->p->fname);
197     zebra_mutex_lock(&lock_list_mutex);
198     yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s refcount=%d",
199             h->p->fd, h, h->p->fname, h->p->ref_count);
200     assert(h->p->ref_count > 0);
201     --(h->p->ref_count);
202     if (h->p->ref_count == 0)
203     {
204         /* must remove shared info from lock_list */
205         struct zebra_lock_info **hp = &lock_list;
206         while (*hp)
207         {
208             if (*hp == h->p)
209             {
210                 *hp = h->p->next;
211                 break;
212             }
213             else
214                 hp = &(*hp)->next;
215         }
216
217         yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s remove",
218                 h->p->fd, h, h->p->fname);
219
220 #ifndef WIN32
221         if (posix_locks)
222             zebra_lock_rdwr_destroy(&h->p->rdwr_lock);
223         zebra_mutex_destroy(&h->p->file_mutex);
224 #endif
225         if (h->p->fd != -1)
226             close(h->p->fd);
227         xfree(h->p->fname);
228         xfree(h->p);
229     }
230     xfree(h);
231     zebra_mutex_unlock(&lock_list_mutex);
232 }
233
234 #ifndef WIN32
235 static int unixLock(int fd, int type, int cmd)
236 {
237     struct flock area;
238     int r;
239     area.l_type = type;
240     area.l_whence = SEEK_SET;
241     area.l_len = area.l_start = 0L;
242
243     yaz_log(log_level, "fcntl begin type=%d fd=%d", type, fd);
244     r = fcntl(fd, cmd, &area);
245     if (r == -1)
246         yaz_log(YLOG_WARN|YLOG_ERRNO, "fcntl FAIL type=%d fd=%d", type, fd);
247     else
248         yaz_log(log_level, "fcntl type=%d OK fd=%d", type, fd);
249     
250     return r;
251 }
252 #endif
253
254 int zebra_lock_w(ZebraLockHandle h)
255 {
256     int r;
257     int do_lock = 0;
258     yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s begin", 
259             h->p->fd, h, h->p->fname);
260
261 #ifdef WIN32
262     while ((r = _locking(h->p->fd, _LK_LOCK, 1)))
263         ;
264 #else
265     if (posix_locks)
266         zebra_lock_rdwr_wlock(&h->p->rdwr_lock);
267
268     zebra_mutex_lock(&h->p->file_mutex);
269     if (h->p->no_file_write_lock == 0)
270         do_lock = 1;
271     h->p->no_file_write_lock++;
272     if (do_lock)
273     {
274         /* if there is already a read lock.. upgrade to write lock */
275         r = unixLock(h->p->fd, F_WRLCK, F_SETLKW);
276     }
277     else
278     {
279         assert(posix_locks);
280     }
281     zebra_mutex_unlock(&h->p->file_mutex);
282
283     h->write_flag = 1;
284 #endif
285     yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s end", 
286             h->p->fd, h, h->p->fname);
287
288     return r;
289 }
290
291 int zebra_lock_r(ZebraLockHandle h)
292 {
293     int r;
294     int do_lock = 0;
295
296     yaz_log(log_level, "zebra_lock_r fd=%d p=%p fname=%s begin", 
297             h->p->fd, h, h->p->fname);
298 #ifdef WIN32
299     while ((r = _locking(h->p->fd, _LK_LOCK, 1)))
300         ;
301 #else
302     if (posix_locks)
303         zebra_lock_rdwr_rlock(&h->p->rdwr_lock);
304
305     zebra_mutex_lock(&h->p->file_mutex);
306     if (h->p->no_file_read_lock == 0 && h->p->no_file_write_lock == 0)
307         do_lock = 1;
308     h->p->no_file_read_lock++;
309     if (do_lock)
310     {
311         /* only read lock if no write locks already */
312         r = unixLock(h->p->fd, F_RDLCK, F_SETLKW);
313     }
314     else
315     {
316         assert(posix_locks);
317     }
318     zebra_mutex_unlock(&h->p->file_mutex);
319     
320     h->write_flag = 0;
321 #endif
322     yaz_log(log_level, "zebra_lock_r fd=%d p=%p fname=%s end", 
323             h->p->fd, h, h->p->fname);
324     return r;
325 }
326
327 int zebra_unlock(ZebraLockHandle h)
328 {
329     int r = 0;
330     int do_unlock = 0;
331     yaz_log(log_level, "zebra_unlock fd=%d p=%p fname=%s begin",
332             h->p->fd, h, h->p->fname);
333 #ifdef WIN32
334     r = _locking(h->p->fd, _LK_UNLCK, 1);
335 #else
336     zebra_mutex_lock(&h->p->file_mutex);
337     if (h->write_flag)
338         h->p->no_file_write_lock--;
339     else
340         h->p->no_file_read_lock--;
341     if (h->p->no_file_read_lock == 0 && h->p->no_file_write_lock == 0)
342         do_unlock = 1;
343     if (do_unlock)
344         r = unixLock(h->p->fd, F_UNLCK, F_SETLKW);
345     else
346     {
347         assert(posix_locks);
348     }
349     zebra_mutex_unlock(&h->p->file_mutex);
350
351     if (posix_locks)
352     {
353         if (h->write_flag)
354             zebra_lock_rdwr_wunlock(&h->p->rdwr_lock);
355         else
356             zebra_lock_rdwr_runlock(&h->p->rdwr_lock);
357     }
358 #endif
359     yaz_log(log_level, "zebra_unlock fd=%d p=%p fname=%s end",
360             h->p->fd, h, h->p->fname);
361     return r;
362 }
363
364 /** \brief see if the fcntl locking is not POSIX 
365  *
366  * The default posix_locks=1 is assumed.. This function sets posix_locks
367  * to zero if linuxthreads is in use.
368  */
369 static int check_for_linuxthreads()
370 {
371 #if __linux
372 #ifdef _CS_GNU_LIBPTHREAD_VERSION
373     char conf_buf[512];
374     size_t r = confstr(_CS_GNU_LIBPTHREAD_VERSION, conf_buf, sizeof(conf_buf));
375     if (r == 0)
376     {
377         yaz_log(YLOG_WARN|YLOG_ERRNO, "confstr failed");
378         return -1;
379     }
380     if (strncmp(conf_buf, "linuxthreads", 12) == 0)
381         posix_locks = 0; /* Using linuxthreads.. */
382 #else
383     posix_locks = 0; /* Old GLIBC on Linux. Assume linuxthreads */
384 #endif
385 #endif
386     return 0;
387 }
388
389 void zebra_flock_init()
390 {
391     if (!initialized)
392     {
393         initialized = 1;
394         log_level = yaz_log_module_level("flock");
395 #if DEBUG_FLOCK
396         log_level = YLOG_LOG|YLOG_FLUSH;
397 #endif
398         check_for_linuxthreads();
399         zebra_mutex_init(&lock_list_mutex);
400         yaz_log(log_level, "posix_locks: %d", posix_locks);
401     }
402     yaz_log(log_level, "zebra_flock_init");
403 }
404
405 /*
406  * Local variables:
407  * c-basic-offset: 4
408  * indent-tabs-mode: nil
409  * End:
410  * vim: shiftwidth=4 tabstop=8 expandtab
411  */
412