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