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