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