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