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