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