]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/alock.c
Sync with HEAD
[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 || errno == EAGAIN) { 
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, (char *)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 ((char *)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
299         if (slot_data.al_appname != NULL) free (slot_data.al_appname);
300         slot_data.al_appname = NULL;
301
302         if (slot_data.al_lock == ALOCK_UNLOCKED) return ALOCK_UNLOCKED;
303
304         res = alock_test_lock (info->al_fd, info->al_slot);
305         if (res < 0) return -1;
306         if (res > 0) {
307                 if (slot_data.al_lock == ALOCK_UNIQUE) {
308                         return ALOCK_UNIQUE;
309                 } else {
310                         return ALOCK_LOCKED;
311                 }
312         }
313         
314         return ALOCK_DIRTY;
315 }
316
317 int 
318 alock_open ( alock_info_t * info,
319              const char * appname,
320              const char * envdir,
321              int locktype )
322 {
323         struct stat statbuf;
324         alock_info_t scan_info;
325         alock_slot_t slot_data;
326         char * filename;
327         int res, max_slot;
328         int dirty_count, live_count;
329
330         assert (info != NULL);
331         assert (appname != NULL);
332         assert (envdir != NULL);
333         assert (locktype >= 1 && locktype <= 2);
334
335         slot_data.al_lock = locktype;
336         slot_data.al_stamp = time(NULL);
337         slot_data.al_pid = getpid();
338         slot_data.al_appname = calloc (1, ALOCK_MAX_APPNAME);
339         strncpy (slot_data.al_appname, appname, ALOCK_MAX_APPNAME-1);
340         slot_data.al_appname [ALOCK_MAX_APPNAME-1] = '\0';
341
342         filename = calloc (1, strlen (envdir) + strlen ("/alock") + 1);
343         strcpy (filename, envdir);
344         strcat (filename, "/alock");
345         info->al_fd = open (filename, O_CREAT|O_RDWR, 0666);
346         free (filename);
347         if (info->al_fd < 0) {
348                 free (slot_data.al_appname);
349                 return ALOCK_UNSTABLE;
350         }
351         info->al_slot = 0;
352
353         res = alock_grab_lock (info->al_fd, 0);
354         if (res == -1) { 
355                 close (info->al_fd);
356                 free (slot_data.al_appname);
357                 return ALOCK_UNSTABLE;
358         }
359
360         res = fstat (info->al_fd, &statbuf);
361         if (res == -1) { 
362                 close (info->al_fd);
363                 free (slot_data.al_appname);
364                 return ALOCK_UNSTABLE;
365         }
366
367         max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
368         dirty_count = 0;
369         live_count = 0;
370         scan_info.al_fd = info->al_fd;
371         for (scan_info.al_slot = 1; 
372              scan_info.al_slot < max_slot;
373              ++ scan_info.al_slot) {
374                 if (scan_info.al_slot != info->al_slot) {
375                         res = alock_query_slot (&scan_info);
376
377                         if (res == ALOCK_UNLOCKED
378                             && info->al_slot == 0) {
379                                 info->al_slot = scan_info.al_slot;
380
381                         } else if (res == ALOCK_LOCKED) {
382                                 ++live_count;
383
384                         } else if (res == ALOCK_UNIQUE
385                                 && locktype == ALOCK_UNIQUE) {
386                                 close (info->al_fd);
387                                 free (slot_data.al_appname);
388                                 return ALOCK_BUSY;
389
390                         } else if (res == ALOCK_DIRTY) {
391                                 ++dirty_count;
392
393                         } else if (res == -1) {
394                                 close (info->al_fd);
395                                 free (slot_data.al_appname);
396                                 return ALOCK_UNSTABLE;
397
398                         }
399                 }
400         }
401
402         if (dirty_count && live_count) {
403                 close (info->al_fd);
404                 free (slot_data.al_appname);
405                 return ALOCK_UNSTABLE;
406         }
407         
408         if (info->al_slot == 0) info->al_slot = max_slot + 1;
409         res = alock_grab_lock (info->al_fd,
410                                info->al_slot);
411         if (res == -1) { 
412                 close (info->al_fd);
413                 free (slot_data.al_appname);
414                 return ALOCK_UNSTABLE;
415         }
416         res = alock_write_slot (info, &slot_data);
417         free (slot_data.al_appname);
418         if (res == -1) { 
419                 close (info->al_fd);
420                 return ALOCK_UNSTABLE;
421         }
422
423         res = alock_release_lock (info->al_fd, 0);
424         if (res == -1) { 
425                 close (info->al_fd);
426                 return ALOCK_UNSTABLE;
427         }
428         
429         if (dirty_count) return ALOCK_RECOVER;
430         return ALOCK_CLEAN;
431 }
432
433 int 
434 alock_scan ( alock_info_t * info )
435 {
436         struct stat statbuf;
437         alock_info_t scan_info;
438         int res, max_slot;
439         int dirty_count, live_count;
440
441         assert (info != NULL);
442
443         scan_info.al_fd = info->al_fd;
444
445         res = alock_grab_lock (info->al_fd, 0);
446         if (res == -1) {
447                 close (info->al_fd);
448                 return ALOCK_UNSTABLE;
449         }
450
451         res = fstat (info->al_fd, &statbuf);
452         if (res == -1) {
453                 close (info->al_fd);
454                 return ALOCK_UNSTABLE;
455         }
456
457         max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
458         dirty_count = 0;
459         live_count = 0;
460         for (scan_info.al_slot = 1; 
461              scan_info.al_slot < max_slot;
462              ++ scan_info.al_slot) {
463                 if (scan_info.al_slot != info->al_slot) {
464                         res = alock_query_slot (&scan_info);
465
466                         if (res == ALOCK_LOCKED) {
467                                 ++live_count;
468                                 
469                         } else if (res == ALOCK_DIRTY) {
470                                 ++dirty_count;
471
472                         } else if (res == -1) {
473                                 close (info->al_fd);
474                                 return ALOCK_UNSTABLE;
475
476                         }
477                 }
478         }
479
480         res = alock_release_lock (info->al_fd, 0);
481         if (res == -1) {
482                 close (info->al_fd);
483                 return ALOCK_UNSTABLE;
484         }
485
486         if (dirty_count) {
487                 if (live_count) {
488                         close (info->al_fd);
489                         return ALOCK_UNSTABLE;
490                 } else {
491                         return ALOCK_RECOVER;
492                 }
493         }
494         
495         return ALOCK_CLEAN;
496 }
497
498 int
499 alock_close ( alock_info_t * info )
500 {
501         alock_slot_t slot_data;
502         int res;
503
504         (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
505
506         res = alock_grab_lock (info->al_fd, 0);
507         if (res == -1) {
508                 close (info->al_fd);
509                 return ALOCK_UNSTABLE;
510         }
511
512         /* mark our slot as clean */
513         res = alock_read_slot (info, &slot_data);
514         if (res == -1) {
515                 close (info->al_fd);
516                 if (slot_data.al_appname != NULL) 
517                         free (slot_data.al_appname);
518                 return ALOCK_UNSTABLE;
519         }
520         slot_data.al_lock = ALOCK_UNLOCKED;
521         res = alock_write_slot (info, &slot_data);
522         if (res == -1) {
523                 close (info->al_fd);
524                 if (slot_data.al_appname != NULL) 
525                         free (slot_data.al_appname);
526                 return ALOCK_UNSTABLE;
527         }
528         if (slot_data.al_appname != NULL) {
529                 free (slot_data.al_appname);
530                 slot_data.al_appname = NULL;
531         }
532
533         res = alock_release_lock (info->al_fd, info->al_slot);
534         if (res == -1) {
535                 close (info->al_fd);
536                 return ALOCK_UNSTABLE;
537         }
538         res = alock_release_lock (info->al_fd, 0);
539         if (res == -1) {
540                 close (info->al_fd);
541                 return ALOCK_UNSTABLE;
542         }
543
544         res = close (info->al_fd);
545         if (res == -1) return ALOCK_UNSTABLE;
546         
547         return ALOCK_CLEAN;
548 }
549
550 int 
551 alock_recover ( alock_info_t * info )
552 {
553         struct stat statbuf;
554         alock_slot_t slot_data;
555         alock_info_t scan_info;
556         int res, max_slot;
557
558         assert (info != NULL);
559
560         scan_info.al_fd = info->al_fd;
561
562         (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
563
564         res = alock_grab_lock (info->al_fd, 0);
565         if (res == -1) {
566                 close (info->al_fd);
567                 return ALOCK_UNSTABLE;
568         }
569
570         res = fstat (info->al_fd, &statbuf);
571         if (res == -1) {
572                 close (info->al_fd);
573                 return ALOCK_UNSTABLE;
574         }
575
576         max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
577         for (scan_info.al_slot = 1; 
578              scan_info.al_slot < max_slot;
579              ++ scan_info.al_slot) {
580                 if (scan_info.al_slot != info->al_slot) {
581                         res = alock_query_slot (&scan_info);
582
583                         if (res == ALOCK_LOCKED
584                             || res == ALOCK_UNIQUE) {
585                                 /* recovery attempt on an active db? */
586                                 close (info->al_fd);
587                                 return ALOCK_UNSTABLE;
588                                 
589                         } else if (res == ALOCK_DIRTY) {
590                                 /* mark it clean */
591                                 res = alock_read_slot (&scan_info, &slot_data);
592                                 if (res == -1) {
593                                         close (info->al_fd);
594                                         return ALOCK_UNSTABLE;
595                                 }
596                                 slot_data.al_lock = ALOCK_UNLOCKED;
597                                 res = alock_write_slot (&scan_info, &slot_data);
598                                 if (res == -1) {
599                                         close (info->al_fd);
600                                         if (slot_data.al_appname != NULL) 
601                                                 free (slot_data.al_appname);
602                                         return ALOCK_UNSTABLE;
603                                 }
604                                 if (slot_data.al_appname != NULL) {
605                                         free (slot_data.al_appname);
606                                         slot_data.al_appname = NULL;
607                                 }
608                                 
609                         } else if (res == -1) {
610                                 close (info->al_fd);
611                                 return ALOCK_UNSTABLE;
612
613                         }
614                 }
615         }
616
617         res = alock_release_lock (info->al_fd, 0);
618         if (res == -1) {
619                 close (info->al_fd);
620                 return ALOCK_UNSTABLE;
621         }
622
623         return ALOCK_CLEAN;
624 }