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