]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/alock.c
document option '-F'
[openldap] / servers / slapd / back-bdb / alock.c
1 /* alock.c - access lock library */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2005 The OpenLDAP Foundation.
6  * Portions Copyright 2004-2005 Symas Corporation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by Matthew Backes at Symas
19  * Corporation for inclusion in OpenLDAP Software.
20  */
21
22 #include "portable.h"
23 #include "alock.h"
24
25 #include <ac/stdlib.h>
26 #include <ac/string.h>
27 #include <ac/unistd.h>
28 #include <ac/errno.h>
29 #include <ac/assert.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/file.h>
33 #include <fcntl.h>
34
35 #ifdef _WIN32
36 #include <stdio.h>
37 #include <io.h>
38 #include <sys/locking.h>
39 #endif
40
41
42 static int
43 alock_grab_lock ( int fd, int slot )
44 {
45         int res;
46         
47 #if defined( HAVE_LOCKF )
48         res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
49         if (res == -1) return -1;
50         res = lockf (fd, F_LOCK, (off_t) ALOCK_SLOT_SIZE);
51 #elif defined( HAVE_FCNTL )
52         struct flock lock_info;
53         (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
54
55         lock_info.l_type = F_WRLCK;
56         lock_info.l_whence = SEEK_SET;
57         lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
58         lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
59
60         res = fcntl (fd, F_SETLKW, &lock_info);
61 #elif defined( _WIN32 )
62         if( _lseek( fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET ) < 0 )
63                 return -1;
64         /*
65          * _lock will try for the lock once per second, returning EDEADLOCK
66          * after ten tries. We just loop until we either get the lock
67          * or some other error is returned.
68          */
69         while((res = _locking( fd, _LK_LOCK, ALOCK_SLOT_SIZE )) < 0 ) {
70                 if( errno != EDEADLOCK )
71                         break;
72         }
73 #else
74 #   error alock needs lockf, fcntl, or _locking
75 #endif
76         if (res == -1) {
77                 assert (errno != EDEADLK);
78                 return -1;
79         }
80         return 0;
81 }
82
83 static int
84 alock_release_lock ( int fd, int slot )
85 {
86         int res;
87         
88 #if defined( HAVE_LOCKF )
89         res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
90         if (res == -1) return -1;
91         res = lockf (fd, F_ULOCK, (off_t) ALOCK_SLOT_SIZE);
92         if (res == -1) return -1;
93 #elif defined ( HAVE_FCNTL )
94         struct flock lock_info;
95         (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
96
97         lock_info.l_type = F_UNLCK;
98         lock_info.l_whence = SEEK_SET;
99         lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
100         lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
101
102         res = fcntl (fd, F_SETLKW, &lock_info);
103         if (res == -1) return -1;
104 #elif defined( _WIN32 )
105         res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET);
106         if (res == -1) return -1;
107         res = _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE );
108         if (res == -1) return -1;
109 #else
110 #   error alock needs lockf, fcntl, or _locking
111 #endif
112
113         return 0;
114 }
115
116 static int
117 alock_test_lock ( int fd, int slot )
118 {
119         int res;
120
121 #if defined( HAVE_LOCKF )
122         res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
123         if (res == -1) return -1;
124
125         res = lockf (fd, F_TEST, (off_t) ALOCK_SLOT_SIZE);
126         if (res == -1) {
127                 if (errno == EACCES) { 
128                         return ALOCK_LOCKED;
129                 } else {
130                         return -1;
131                 }
132         }
133 #elif defined( HAVE_FCNTL )
134         struct flock lock_info;
135         (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
136
137         lock_info.l_type = F_WRLCK;
138         lock_info.l_whence = SEEK_SET;
139         lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
140         lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
141
142         res = fcntl (fd, F_GETLK, &lock_info);
143         if (res == -1) return -1;
144
145         if (lock_info.l_type != F_UNLCK) return ALOCK_LOCKED;
146 #elif defined( _WIN32 )
147         res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET);
148         if (res == -1) return -1;
149         res = _locking( fd, _LK_NBLCK, ALOCK_SLOT_SIZE );
150         _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE );
151         if (res == -1) {
152            if( errno == EACCES ) {
153                    return ALOCK_LOCKED;
154            } else {
155                    return -1;
156            }
157         }
158 #else
159 #   error alock needs lockf, fcntl, or _locking
160 #endif
161         
162         return 0;
163 }
164
165 /* Read a 64bit LE value */
166 static unsigned long int
167 alock_read_iattr ( unsigned char * bufptr )
168 {
169         unsigned long int val = 0;
170         int count;
171
172         assert (bufptr != NULL);
173
174         bufptr += sizeof (unsigned long int);
175         for (count=0; count <= sizeof (unsigned long int); ++count) {
176                 val <<= 8;
177                 val += (unsigned long int) *bufptr--;
178         }
179
180         return val;
181 }
182
183 /* Write a 64bit LE value */
184 static void
185 alock_write_iattr ( unsigned char * bufptr,
186                     unsigned long int val )
187 {
188         int count;
189
190         assert (bufptr != NULL);
191
192         for (count=0; count < 8; ++count) {
193                 *bufptr++ = (unsigned char) (val & 0xff);
194                 val >>= 8;
195         }
196 }
197
198 static int
199 alock_read_slot ( alock_info_t * info,
200                   alock_slot_t * slot_data )
201 {
202         unsigned char slotbuf [ALOCK_SLOT_SIZE];
203         int res, size, size_total, err;
204
205         assert (info != NULL);
206         assert (slot_data != NULL);
207         assert (info->al_slot > 0);
208
209         res = lseek (info->al_fd, 
210                      (off_t) (ALOCK_SLOT_SIZE * info->al_slot), 
211                      SEEK_SET);
212         if (res == -1) return -1;
213
214         size_total = 0;
215         while (size_total < ALOCK_SLOT_SIZE) {
216                 size = read (info->al_fd, 
217                              slotbuf + size_total, 
218                              ALOCK_SLOT_SIZE - size_total);
219                 if (size == 0) return -1;
220                 if (size < 0) {
221                         err = errno;
222                         if (err != EINTR && err != EAGAIN) return -1;
223                 } else {
224                         size_total += size;
225                 }
226         }
227         
228         if (alock_read_iattr (slotbuf) != ALOCK_MAGIC) {
229                 return 1;
230         }
231         slot_data->al_lock  = alock_read_iattr (slotbuf+8);
232         slot_data->al_stamp = alock_read_iattr (slotbuf+16);
233         slot_data->al_pid   = alock_read_iattr (slotbuf+24);
234
235         if (slot_data->al_appname) free (slot_data->al_appname);
236         slot_data->al_appname = calloc (1, ALOCK_MAX_APPNAME);
237         strncpy (slot_data->al_appname, slotbuf+32, ALOCK_MAX_APPNAME-1);
238         (slot_data->al_appname) [ALOCK_MAX_APPNAME-1] = '\0';
239
240         return 0;
241 }
242
243 static int
244 alock_write_slot ( alock_info_t * info,
245                    alock_slot_t * slot_data )
246 {
247         unsigned char slotbuf [ALOCK_SLOT_SIZE];
248         int res, size, size_total, err;
249
250         assert (info != NULL);
251         assert (slot_data != NULL);
252         assert (info->al_slot > 0);
253
254         (void) memset ((void *) slotbuf, 0, ALOCK_SLOT_SIZE);
255         
256         alock_write_iattr (slotbuf,    ALOCK_MAGIC);
257         assert (alock_read_iattr (slotbuf) == ALOCK_MAGIC);
258         alock_write_iattr (slotbuf+8,  slot_data->al_lock);
259         alock_write_iattr (slotbuf+16, slot_data->al_stamp);
260         alock_write_iattr (slotbuf+24, slot_data->al_pid);
261
262         strncpy (slotbuf+32, slot_data->al_appname, ALOCK_MAX_APPNAME-1);
263         slotbuf[ALOCK_SLOT_SIZE-1] = '\0';
264
265         res = lseek (info->al_fd, 
266                      (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
267                      SEEK_SET);
268         if (res == -1) return -1;
269
270         size_total = 0;
271         while (size_total < ALOCK_SLOT_SIZE) {
272                 size = write (info->al_fd, 
273                               slotbuf + size_total, 
274                               ALOCK_SLOT_SIZE - size_total);
275                 if (size == 0) return -1;
276                 if (size < 0) {
277                         err = errno;
278                         if (err != EINTR && err != EAGAIN) return -1;
279                 } else {
280                         size_total += size;
281                 }
282         }
283         
284         return 0;
285 }
286
287 static int
288 alock_query_slot ( alock_info_t * info )
289 {
290         int res;
291         alock_slot_t slot_data;
292
293         assert (info != NULL);
294         assert (info->al_slot > 0);
295         
296         (void) memset ((void *) &slot_data, 0, sizeof (alock_slot_t));
297         alock_read_slot (info, &slot_data);
298         if (slot_data.al_lock == ALOCK_UNLOCKED) return ALOCK_UNLOCKED;
299
300         if (slot_data.al_appname != NULL) free (slot_data.al_appname);
301         slot_data.al_appname = NULL;
302
303         res = alock_test_lock (info->al_fd, info->al_slot);
304         if (res < 0) return -1;
305         if (res > 0) {
306                 if (slot_data.al_lock == ALOCK_UNIQUE) {
307                         return ALOCK_UNIQUE;
308                 } else {
309                         return ALOCK_LOCKED;
310                 }
311         }
312         
313         return ALOCK_DIRTY;
314 }
315
316 int 
317 alock_open ( alock_info_t * info,
318              const char * appname,
319              const char * envdir,
320              int locktype )
321 {
322         struct stat statbuf;
323         alock_info_t scan_info;
324         alock_slot_t slot_data;
325         char * filename;
326         int res, max_slot;
327         int dirty_count, live_count;
328
329         assert (info != NULL);
330         assert (appname != NULL);
331         assert (envdir != NULL);
332         assert (locktype >= 1 && locktype <= 2);
333
334         slot_data.al_lock = locktype;
335         slot_data.al_stamp = time(NULL);
336         slot_data.al_pid = getpid();
337         slot_data.al_appname = calloc (1, ALOCK_MAX_APPNAME);
338         strncpy (slot_data.al_appname, appname, ALOCK_MAX_APPNAME-1);
339         slot_data.al_appname [ALOCK_MAX_APPNAME-1] = '\0';
340
341         filename = calloc (1, strlen (envdir) + strlen ("/alock") + 1);
342         strcpy (filename, envdir);
343         strcat (filename, "/alock");
344         info->al_fd = open (filename, O_CREAT|O_RDWR, 0666);
345         free (filename);
346         if (info->al_fd < 0) {
347                 free (slot_data.al_appname);
348                 return ALOCK_UNSTABLE;
349         }
350         info->al_slot = 0;
351
352         res = alock_grab_lock (info->al_fd, 0);
353         if (res == -1) { 
354                 close (info->al_fd);
355                 free (slot_data.al_appname);
356                 return ALOCK_UNSTABLE;
357         }
358
359         res = fstat (info->al_fd, &statbuf);
360         if (res == -1) { 
361                 close (info->al_fd);
362                 free (slot_data.al_appname);
363                 return ALOCK_UNSTABLE;
364         }
365
366         max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
367         dirty_count = 0;
368         live_count = 0;
369         scan_info.al_fd = info->al_fd;
370         for (scan_info.al_slot = 1; 
371              scan_info.al_slot < max_slot;
372              ++ scan_info.al_slot) {
373                 if (scan_info.al_slot != info->al_slot) {
374                         res = alock_query_slot (&scan_info);
375
376                         if (res == ALOCK_UNLOCKED
377                             && info->al_slot == 0) {
378                                 info->al_slot = scan_info.al_slot;
379
380                         } else if (res == ALOCK_LOCKED) {
381                                 ++live_count;
382
383                         } else if (res == ALOCK_UNIQUE
384                                 && locktype == ALOCK_UNIQUE) {
385                                 close (info->al_fd);
386                                 free (slot_data.al_appname);
387                                 return ALOCK_BUSY;
388
389                         } else if (res == ALOCK_DIRTY) {
390                                 ++dirty_count;
391
392                         } else if (res == -1) {
393                                 close (info->al_fd);
394                                 free (slot_data.al_appname);
395                                 return ALOCK_UNSTABLE;
396
397                         }
398                 }
399         }
400
401         if (dirty_count && live_count) {
402                 close (info->al_fd);
403                 free (slot_data.al_appname);
404                 return ALOCK_UNSTABLE;
405         }
406         
407         if (info->al_slot == 0) info->al_slot = max_slot + 1;
408         res = alock_grab_lock (info->al_fd,
409                                info->al_slot);
410         if (res == -1) { 
411                 close (info->al_fd);
412                 free (slot_data.al_appname);
413                 return ALOCK_UNSTABLE;
414         }
415         res = alock_write_slot (info, &slot_data);
416         free (slot_data.al_appname);
417         if (res == -1) { 
418                 close (info->al_fd);
419                 return ALOCK_UNSTABLE;
420         }
421
422         res = alock_release_lock (info->al_fd, 0);
423         if (res == -1) { 
424                 close (info->al_fd);
425                 return ALOCK_UNSTABLE;
426         }
427         
428         if (dirty_count) return ALOCK_RECOVER;
429         return ALOCK_CLEAN;
430 }
431
432 int 
433 alock_scan ( alock_info_t * info )
434 {
435         struct stat statbuf;
436         alock_info_t scan_info;
437         int res, max_slot;
438         int dirty_count, live_count;
439
440         assert (info != NULL);
441
442         scan_info.al_fd = info->al_fd;
443
444         res = alock_grab_lock (info->al_fd, 0);
445         if (res == -1) {
446                 close (info->al_fd);
447                 return ALOCK_UNSTABLE;
448         }
449
450         res = fstat (info->al_fd, &statbuf);
451         if (res == -1) {
452                 close (info->al_fd);
453                 return ALOCK_UNSTABLE;
454         }
455
456         max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
457         dirty_count = 0;
458         live_count = 0;
459         for (scan_info.al_slot = 1; 
460              scan_info.al_slot < max_slot;
461              ++ scan_info.al_slot) {
462                 if (scan_info.al_slot != info->al_slot) {
463                         res = alock_query_slot (&scan_info);
464
465                         if (res == ALOCK_LOCKED) {
466                                 ++live_count;
467                                 
468                         } else if (res == ALOCK_DIRTY) {
469                                 ++dirty_count;
470
471                         } else if (res == -1) {
472                                 close (info->al_fd);
473                                 return ALOCK_UNSTABLE;
474
475                         }
476                 }
477         }
478
479         res = alock_release_lock (info->al_fd, 0);
480         if (res == -1) {
481                 close (info->al_fd);
482                 return ALOCK_UNSTABLE;
483         }
484
485         if (dirty_count) {
486                 if (live_count) {
487                         close (info->al_fd);
488                         return ALOCK_UNSTABLE;
489                 } else {
490                         return ALOCK_RECOVER;
491                 }
492         }
493         
494         return ALOCK_CLEAN;
495 }
496
497 int
498 alock_close ( alock_info_t * info )
499 {
500         alock_slot_t slot_data;
501         int res;
502
503         (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
504
505         res = alock_grab_lock (info->al_fd, 0);
506         if (res == -1) {
507                 close (info->al_fd);
508                 return ALOCK_UNSTABLE;
509         }
510
511         /* mark our slot as clean */
512         res = alock_read_slot (info, &slot_data);
513         if (res == -1) {
514                 close (info->al_fd);
515                 if (slot_data.al_appname != NULL) 
516                         free (slot_data.al_appname);
517                 return ALOCK_UNSTABLE;
518         }
519         slot_data.al_lock = ALOCK_UNLOCKED;
520         res = alock_write_slot (info, &slot_data);
521         if (res == -1) {
522                 close (info->al_fd);
523                 if (slot_data.al_appname != NULL) 
524                         free (slot_data.al_appname);
525                 return ALOCK_UNSTABLE;
526         }
527         if (slot_data.al_appname != NULL) {
528                 free (slot_data.al_appname);
529                 slot_data.al_appname = NULL;
530         }
531
532         res = alock_release_lock (info->al_fd, info->al_slot);
533         if (res == -1) {
534                 close (info->al_fd);
535                 return ALOCK_UNSTABLE;
536         }
537         res = alock_release_lock (info->al_fd, 0);
538         if (res == -1) {
539                 close (info->al_fd);
540                 return ALOCK_UNSTABLE;
541         }
542
543         res = close (info->al_fd);
544         if (res == -1) return ALOCK_UNSTABLE;
545         
546         return ALOCK_CLEAN;
547 }
548
549 int 
550 alock_recover ( alock_info_t * info )
551 {
552         struct stat statbuf;
553         alock_slot_t slot_data;
554         alock_info_t scan_info;
555         int res, max_slot;
556
557         assert (info != NULL);
558
559         scan_info.al_fd = info->al_fd;
560
561         (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
562
563         res = alock_grab_lock (info->al_fd, 0);
564         if (res == -1) {
565                 close (info->al_fd);
566                 return ALOCK_UNSTABLE;
567         }
568
569         res = fstat (info->al_fd, &statbuf);
570         if (res == -1) {
571                 close (info->al_fd);
572                 return ALOCK_UNSTABLE;
573         }
574
575         max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
576         for (scan_info.al_slot = 1; 
577              scan_info.al_slot < max_slot;
578              ++ scan_info.al_slot) {
579                 if (scan_info.al_slot != info->al_slot) {
580                         res = alock_query_slot (&scan_info);
581
582                         if (res == ALOCK_LOCKED
583                             || res == ALOCK_UNIQUE) {
584                                 /* recovery attempt on an active db? */
585                                 close (info->al_fd);
586                                 return ALOCK_UNSTABLE;
587                                 
588                         } else if (res == ALOCK_DIRTY) {
589                                 /* mark it clean */
590                                 res = alock_read_slot (&scan_info, &slot_data);
591                                 if (res == -1) {
592                                         close (info->al_fd);
593                                         return ALOCK_UNSTABLE;
594                                 }
595                                 slot_data.al_lock = ALOCK_UNLOCKED;
596                                 res = alock_write_slot (&scan_info, &slot_data);
597                                 if (res == -1) {
598                                         close (info->al_fd);
599                                         if (slot_data.al_appname != NULL) 
600                                                 free (slot_data.al_appname);
601                                         return ALOCK_UNSTABLE;
602                                 }
603                                 if (slot_data.al_appname != NULL) {
604                                         free (slot_data.al_appname);
605                                         slot_data.al_appname = NULL;
606                                 }
607                                 
608                         } else if (res == -1) {
609                                 close (info->al_fd);
610                                 return ALOCK_UNSTABLE;
611
612                         }
613                 }
614         }
615
616         res = alock_release_lock (info->al_fd, 0);
617         if (res == -1) {
618                 close (info->al_fd);
619                 return ALOCK_UNSTABLE;
620         }
621
622         return ALOCK_CLEAN;
623 }