s3:modules:vfs_virusfilter: Recent talloc changes cause infinite start-up failure
[samba.git] / lib / tdb / common / mutex.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    trivial database library
5
6    Copyright (C) Volker Lendecke 2012,2013
7    Copyright (C) Stefan Metzmacher 2013,2014
8    Copyright (C) Michael Adam 2014
9
10      ** NOTE! The following LGPL license applies to the tdb
11      ** library. This does NOT imply that all of Samba is released
12      ** under the LGPL
13
14    This library is free software; you can redistribute it and/or
15    modify it under the terms of the GNU Lesser General Public
16    License as published by the Free Software Foundation; either
17    version 3 of the License, or (at your option) any later version.
18
19    This library is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22    Lesser General Public License for more details.
23
24    You should have received a copy of the GNU Lesser General Public
25    License along with this library; if not, see <http://www.gnu.org/licenses/>.
26 */
27 #include "tdb_private.h"
28 #include "system/threads.h"
29
30 #ifdef USE_TDB_MUTEX_LOCKING
31
32 /*
33  * If we run with mutexes, we store the "struct tdb_mutexes" at the
34  * beginning of the file. We store an additional tdb_header right
35  * beyond the mutex area, page aligned. All the offsets within the tdb
36  * are relative to the area behind the mutex area. tdb->map_ptr points
37  * behind the mmap area as well, so the read and write path in the
38  * mutex case can remain unchanged.
39  *
40  * Early in the mutex development the mutexes were placed between the hash
41  * chain pointers and the real tdb data. This had two drawbacks: First, it
42  * made pointer calculations more complex. Second, we had to mmap the mutex
43  * area twice. One was the normal map_ptr in the tdb. This frequently changed
44  * from within tdb_oob. At least the Linux glibc robust mutex code assumes
45  * constant pointers in memory, so a constantly changing mmap area destroys
46  * the mutex list. So we had to mmap the first bytes of the file with a second
47  * mmap call. With that scheme, very weird errors happened that could be
48  * easily fixed by doing the mutex mmap in a second file. It seemed that
49  * mapping the same memory area twice does not end up in accessing the same
50  * physical page, looking at the mutexes in gdb it seemed that old data showed
51  * up after some re-mapping. To avoid a separate mutex file, the code now puts
52  * the real content of the tdb file after the mutex area. This way we do not
53  * have overlapping mmap areas, the mutex area is mmapped once and not
54  * changed, the tdb data area's mmap is constantly changed but does not
55  * overlap.
56  */
57
58 struct tdb_mutexes {
59         struct tdb_header hdr;
60
61         /* protect allrecord_lock */
62         pthread_mutex_t allrecord_mutex;
63
64         /*
65          * F_UNLCK: free,
66          * F_RDLCK: shared,
67          * F_WRLCK: exclusive
68          */
69         short int allrecord_lock;
70
71         /*
72          * Index 0 is the freelist mutex, followed by
73          * one mutex per hashchain.
74          */
75         pthread_mutex_t hashchains[1];
76 };
77
78 bool tdb_have_mutexes(struct tdb_context *tdb)
79 {
80         return ((tdb->feature_flags & TDB_FEATURE_FLAG_MUTEX) != 0);
81 }
82
83 size_t tdb_mutex_size(struct tdb_context *tdb)
84 {
85         size_t mutex_size;
86
87         if (!tdb_have_mutexes(tdb)) {
88                 return 0;
89         }
90
91         mutex_size = sizeof(struct tdb_mutexes);
92         mutex_size += tdb->hash_size * sizeof(pthread_mutex_t);
93
94         return TDB_ALIGN(mutex_size, tdb->page_size);
95 }
96
97 /*
98  * Get the index for a chain mutex
99  */
100 static bool tdb_mutex_index(struct tdb_context *tdb, off_t off, off_t len,
101                             unsigned *idx)
102 {
103         /*
104          * Weird but true: We fcntl lock 1 byte at an offset 4 bytes before
105          * the 4 bytes of the freelist start and the hash chain that is about
106          * to be locked. See lock_offset() where the freelist is -1 vs the
107          * "+1" in TDB_HASH_TOP(). Because the mutex array is represented in
108          * the tdb file itself as data, we need to adjust the offset here.
109          */
110         const off_t freelist_lock_ofs = FREELIST_TOP - sizeof(tdb_off_t);
111
112         if (!tdb_have_mutexes(tdb)) {
113                 return false;
114         }
115         if (len != 1) {
116                 /* Possibly the allrecord lock */
117                 return false;
118         }
119         if (off < freelist_lock_ofs) {
120                 /* One of the special locks */
121                 return false;
122         }
123         if (tdb->hash_size == 0) {
124                 /* tdb not initialized yet, called from tdb_open_ex() */
125                 return false;
126         }
127         if (off >= TDB_DATA_START(tdb->hash_size)) {
128                 /* Single record lock from traverses */
129                 return false;
130         }
131
132         /*
133          * Now we know it's a freelist or hash chain lock. Those are always 4
134          * byte aligned. Paranoia check.
135          */
136         if ((off % sizeof(tdb_off_t)) != 0) {
137                 abort();
138         }
139
140         /*
141          * Re-index the fcntl offset into an offset into the mutex array
142          */
143         off -= freelist_lock_ofs; /* rebase to index 0 */
144         off /= sizeof(tdb_off_t); /* 0 for freelist 1-n for hashchain */
145
146         *idx = off;
147         return true;
148 }
149
150 static bool tdb_have_mutex_chainlocks(struct tdb_context *tdb)
151 {
152         int i;
153
154         for (i=0; i < tdb->num_lockrecs; i++) {
155                 bool ret;
156                 unsigned idx;
157
158                 ret = tdb_mutex_index(tdb,
159                                       tdb->lockrecs[i].off,
160                                       tdb->lockrecs[i].count,
161                                       &idx);
162                 if (!ret) {
163                         continue;
164                 }
165
166                 if (idx == 0) {
167                         /* this is the freelist mutex */
168                         continue;
169                 }
170
171                 return true;
172         }
173
174         return false;
175 }
176
177 static int chain_mutex_lock(pthread_mutex_t *m, bool waitflag)
178 {
179         int ret;
180
181         if (waitflag) {
182                 ret = pthread_mutex_lock(m);
183         } else {
184                 ret = pthread_mutex_trylock(m);
185         }
186         if (ret != EOWNERDEAD) {
187                 return ret;
188         }
189
190         /*
191          * For chainlocks, we don't do any cleanup (yet?)
192          */
193         return pthread_mutex_consistent(m);
194 }
195
196 static int allrecord_mutex_lock(struct tdb_mutexes *m, bool waitflag)
197 {
198         int ret;
199
200         if (waitflag) {
201                 ret = pthread_mutex_lock(&m->allrecord_mutex);
202         } else {
203                 ret = pthread_mutex_trylock(&m->allrecord_mutex);
204         }
205         if (ret != EOWNERDEAD) {
206                 return ret;
207         }
208
209         /*
210          * The allrecord lock holder died. We need to reset the allrecord_lock
211          * to F_UNLCK. This should also be the indication for
212          * tdb_needs_recovery.
213          */
214         m->allrecord_lock = F_UNLCK;
215
216         return pthread_mutex_consistent(&m->allrecord_mutex);
217 }
218
219 bool tdb_mutex_lock(struct tdb_context *tdb, int rw, off_t off, off_t len,
220                     bool waitflag, int *pret)
221 {
222         struct tdb_mutexes *m = tdb->mutexes;
223         pthread_mutex_t *chain;
224         int ret;
225         unsigned idx;
226         bool allrecord_ok;
227
228         if (!tdb_mutex_index(tdb, off, len, &idx)) {
229                 return false;
230         }
231         chain = &m->hashchains[idx];
232
233 again:
234         ret = chain_mutex_lock(chain, waitflag);
235         if (ret == EBUSY) {
236                 ret = EAGAIN;
237         }
238         if (ret != 0) {
239                 errno = ret;
240                 goto fail;
241         }
242
243         if (idx == 0) {
244                 /*
245                  * This is a freelist lock, which is independent to
246                  * the allrecord lock. So we're done once we got the
247                  * freelist mutex.
248                  */
249                 *pret = 0;
250                 return true;
251         }
252
253         if (tdb_have_mutex_chainlocks(tdb)) {
254                 /*
255                  * We can only check the allrecord lock once. If we do it with
256                  * one chain mutex locked, we will deadlock with the allrecord
257                  * locker process in the following way: We lock the first hash
258                  * chain, we check for the allrecord lock. We keep the hash
259                  * chain locked. Then the allrecord locker locks the
260                  * allrecord_mutex. It walks the list of chain mutexes,
261                  * locking them all in sequence. Meanwhile, we have the chain
262                  * mutex locked, so the allrecord locker blocks trying to lock
263                  * our chain mutex. Then we come in and try to lock the second
264                  * chain lock, which in most cases will be the freelist. We
265                  * see that the allrecord lock is locked and put ourselves on
266                  * the allrecord_mutex. This will never be signalled though
267                  * because the allrecord locker waits for us to give up the
268                  * chain lock.
269                  */
270
271                 *pret = 0;
272                 return true;
273         }
274
275         /*
276          * Check if someone is has the allrecord lock: queue if so.
277          */
278
279         allrecord_ok = false;
280
281         if (m->allrecord_lock == F_UNLCK) {
282                 /*
283                  * allrecord lock not taken
284                  */
285                 allrecord_ok = true;
286         }
287
288         if ((m->allrecord_lock == F_RDLCK) && (rw == F_RDLCK)) {
289                 /*
290                  * allrecord shared lock taken, but we only want to read
291                  */
292                 allrecord_ok = true;
293         }
294
295         if (allrecord_ok) {
296                 *pret = 0;
297                 return true;
298         }
299
300         ret = pthread_mutex_unlock(chain);
301         if (ret != 0) {
302                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "pthread_mutex_unlock"
303                          "(chain_mutex) failed: %s\n", strerror(ret)));
304                 errno = ret;
305                 goto fail;
306         }
307         ret = allrecord_mutex_lock(m, waitflag);
308         if (ret == EBUSY) {
309                 ret = EAGAIN;
310         }
311         if (ret != 0) {
312                 if (waitflag || (ret != EAGAIN)) {
313                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "pthread_mutex_%slock"
314                                  "(allrecord_mutex) failed: %s\n",
315                                  waitflag ? "" : "try_",  strerror(ret)));
316                 }
317                 errno = ret;
318                 goto fail;
319         }
320         ret = pthread_mutex_unlock(&m->allrecord_mutex);
321         if (ret != 0) {
322                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "pthread_mutex_unlock"
323                          "(allrecord_mutex) failed: %s\n", strerror(ret)));
324                 errno = ret;
325                 goto fail;
326         }
327         goto again;
328
329 fail:
330         *pret = -1;
331         return true;
332 }
333
334 bool tdb_mutex_unlock(struct tdb_context *tdb, int rw, off_t off, off_t len,
335                       int *pret)
336 {
337         struct tdb_mutexes *m = tdb->mutexes;
338         pthread_mutex_t *chain;
339         int ret;
340         unsigned idx;
341
342         if (!tdb_mutex_index(tdb, off, len, &idx)) {
343                 return false;
344         }
345         chain = &m->hashchains[idx];
346
347         ret = pthread_mutex_unlock(chain);
348         if (ret == 0) {
349                 *pret = 0;
350                 return true;
351         }
352         errno = ret;
353         *pret = -1;
354         return true;
355 }
356
357 int tdb_mutex_allrecord_lock(struct tdb_context *tdb, int ltype,
358                              enum tdb_lock_flags flags)
359 {
360         struct tdb_mutexes *m = tdb->mutexes;
361         int ret;
362         uint32_t i;
363         bool waitflag = (flags & TDB_LOCK_WAIT);
364         int saved_errno;
365
366         if (tdb->flags & TDB_NOLOCK) {
367                 return 0;
368         }
369
370         if (flags & TDB_LOCK_MARK_ONLY) {
371                 return 0;
372         }
373
374         ret = allrecord_mutex_lock(m, waitflag);
375         if (!waitflag && (ret == EBUSY)) {
376                 errno = EAGAIN;
377                 tdb->ecode = TDB_ERR_LOCK;
378                 return -1;
379         }
380         if (ret != 0) {
381                 if (!(flags & TDB_LOCK_PROBE)) {
382                         TDB_LOG((tdb, TDB_DEBUG_TRACE,
383                                  "allrecord_mutex_lock() failed: %s\n",
384                                  strerror(ret)));
385                 }
386                 tdb->ecode = TDB_ERR_LOCK;
387                 return -1;
388         }
389
390         if (m->allrecord_lock != F_UNLCK) {
391                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "allrecord_lock == %d\n",
392                          (int)m->allrecord_lock));
393                 goto fail_unlock_allrecord_mutex;
394         }
395         m->allrecord_lock = (ltype == F_RDLCK) ? F_RDLCK : F_WRLCK;
396
397         for (i=0; i<tdb->hash_size; i++) {
398
399                 /* ignore hashchains[0], the freelist */
400                 pthread_mutex_t *chain = &m->hashchains[i+1];
401
402                 ret = chain_mutex_lock(chain, waitflag);
403                 if (!waitflag && (ret == EBUSY)) {
404                         errno = EAGAIN;
405                         goto fail_unroll_allrecord_lock;
406                 }
407                 if (ret != 0) {
408                         if (!(flags & TDB_LOCK_PROBE)) {
409                                 TDB_LOG((tdb, TDB_DEBUG_TRACE,
410                                          "chain_mutex_lock() failed: %s\n",
411                                          strerror(ret)));
412                         }
413                         errno = ret;
414                         goto fail_unroll_allrecord_lock;
415                 }
416
417                 ret = pthread_mutex_unlock(chain);
418                 if (ret != 0) {
419                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "pthread_mutex_unlock"
420                                  "(chainlock) failed: %s\n", strerror(ret)));
421                         errno = ret;
422                         goto fail_unroll_allrecord_lock;
423                 }
424         }
425         /*
426          * We leave this routine with m->allrecord_mutex locked
427          */
428         return 0;
429
430 fail_unroll_allrecord_lock:
431         m->allrecord_lock = F_UNLCK;
432
433 fail_unlock_allrecord_mutex:
434         saved_errno = errno;
435         ret = pthread_mutex_unlock(&m->allrecord_mutex);
436         if (ret != 0) {
437                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "pthread_mutex_unlock"
438                          "(allrecord_mutex) failed: %s\n", strerror(ret)));
439         }
440         errno = saved_errno;
441         tdb->ecode = TDB_ERR_LOCK;
442         return -1;
443 }
444
445 int tdb_mutex_allrecord_upgrade(struct tdb_context *tdb)
446 {
447         struct tdb_mutexes *m = tdb->mutexes;
448         int ret;
449         uint32_t i;
450
451         if (tdb->flags & TDB_NOLOCK) {
452                 return 0;
453         }
454
455         /*
456          * Our only caller tdb_allrecord_upgrade()
457          * garantees that we already own the allrecord lock.
458          *
459          * Which means m->allrecord_mutex is still locked by us.
460          */
461
462         if (m->allrecord_lock != F_RDLCK) {
463                 tdb->ecode = TDB_ERR_LOCK;
464                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "allrecord_lock == %d\n",
465                          (int)m->allrecord_lock));
466                 return -1;
467         }
468
469         m->allrecord_lock = F_WRLCK;
470
471         for (i=0; i<tdb->hash_size; i++) {
472
473                 /* ignore hashchains[0], the freelist */
474                 pthread_mutex_t *chain = &m->hashchains[i+1];
475
476                 ret = chain_mutex_lock(chain, true);
477                 if (ret != 0) {
478                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "pthread_mutex_lock"
479                                  "(chainlock) failed: %s\n", strerror(ret)));
480                         goto fail_unroll_allrecord_lock;
481                 }
482
483                 ret = pthread_mutex_unlock(chain);
484                 if (ret != 0) {
485                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "pthread_mutex_unlock"
486                                  "(chainlock) failed: %s\n", strerror(ret)));
487                         goto fail_unroll_allrecord_lock;
488                 }
489         }
490
491         return 0;
492
493 fail_unroll_allrecord_lock:
494         m->allrecord_lock = F_RDLCK;
495         tdb->ecode = TDB_ERR_LOCK;
496         return -1;
497 }
498
499 void tdb_mutex_allrecord_downgrade(struct tdb_context *tdb)
500 {
501         struct tdb_mutexes *m = tdb->mutexes;
502
503         /*
504          * Our only caller tdb_allrecord_upgrade() (in the error case)
505          * garantees that we already own the allrecord lock.
506          *
507          * Which means m->allrecord_mutex is still locked by us.
508          */
509
510         if (m->allrecord_lock != F_WRLCK) {
511                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "allrecord_lock == %d\n",
512                          (int)m->allrecord_lock));
513                 return;
514         }
515
516         m->allrecord_lock = F_RDLCK;
517         return;
518 }
519
520
521 int tdb_mutex_allrecord_unlock(struct tdb_context *tdb)
522 {
523         struct tdb_mutexes *m = tdb->mutexes;
524         short old;
525         int ret;
526
527         if (tdb->flags & TDB_NOLOCK) {
528                 return 0;
529         }
530
531         /*
532          * Our only callers tdb_allrecord_unlock() and
533          * tdb_allrecord_lock() (in the error path)
534          * garantee that we already own the allrecord lock.
535          *
536          * Which means m->allrecord_mutex is still locked by us.
537          */
538
539         if ((m->allrecord_lock != F_RDLCK) && (m->allrecord_lock != F_WRLCK)) {
540                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "allrecord_lock == %d\n",
541                          (int)m->allrecord_lock));
542                 return -1;
543         }
544
545         old = m->allrecord_lock;
546         m->allrecord_lock = F_UNLCK;
547
548         ret = pthread_mutex_unlock(&m->allrecord_mutex);
549         if (ret != 0) {
550                 m->allrecord_lock = old;
551                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "pthread_mutex_unlock"
552                          "(allrecord_mutex) failed: %s\n", strerror(ret)));
553                 return -1;
554         }
555         return 0;
556 }
557
558 int tdb_mutex_init(struct tdb_context *tdb)
559 {
560         struct tdb_mutexes *m;
561         pthread_mutexattr_t ma;
562         uint32_t i;
563         int ret;
564
565         ret = tdb_mutex_mmap(tdb);
566         if (ret == -1) {
567                 return -1;
568         }
569         m = tdb->mutexes;
570
571         ret = pthread_mutexattr_init(&ma);
572         if (ret != 0) {
573                 goto fail_munmap;
574         }
575         ret = pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK);
576         if (ret != 0) {
577                 goto fail;
578         }
579         ret = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED);
580         if (ret != 0) {
581                 goto fail;
582         }
583         ret = pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST);
584         if (ret != 0) {
585                 goto fail;
586         }
587
588         for (i=0; i<tdb->hash_size+1; i++) {
589                 pthread_mutex_t *chain = &m->hashchains[i];
590
591                 ret = pthread_mutex_init(chain, &ma);
592                 if (ret != 0) {
593                         goto fail;
594                 }
595         }
596
597         m->allrecord_lock = F_UNLCK;
598
599         ret = pthread_mutex_init(&m->allrecord_mutex, &ma);
600         if (ret != 0) {
601                 goto fail;
602         }
603         ret = 0;
604 fail:
605         pthread_mutexattr_destroy(&ma);
606 fail_munmap:
607
608         if (ret == 0) {
609                 return 0;
610         }
611
612         tdb_mutex_munmap(tdb);
613
614         errno = ret;
615         return -1;
616 }
617
618 int tdb_mutex_mmap(struct tdb_context *tdb)
619 {
620         size_t len;
621         void *ptr;
622
623         len = tdb_mutex_size(tdb);
624         if (len == 0) {
625                 return 0;
626         }
627
628         if (tdb->mutexes != NULL) {
629                 return 0;
630         }
631
632         ptr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FILE,
633                    tdb->fd, 0);
634         if (ptr == MAP_FAILED) {
635                 return -1;
636         }
637         tdb->mutexes = (struct tdb_mutexes *)ptr;
638
639         return 0;
640 }
641
642 int tdb_mutex_munmap(struct tdb_context *tdb)
643 {
644         size_t len;
645         int ret;
646
647         len = tdb_mutex_size(tdb);
648         if (len == 0) {
649                 return 0;
650         }
651
652         ret = munmap(tdb->mutexes, len);
653         if (ret == -1) {
654                 return -1;
655         }
656         tdb->mutexes = NULL;
657
658         return 0;
659 }
660
661 static bool tdb_mutex_locking_cached;
662
663 static bool tdb_mutex_locking_supported(void)
664 {
665         pthread_mutexattr_t ma;
666         pthread_mutex_t m;
667         int ret;
668         static bool initialized;
669
670         if (initialized) {
671                 return tdb_mutex_locking_cached;
672         }
673
674         initialized = true;
675
676         ret = pthread_mutexattr_init(&ma);
677         if (ret != 0) {
678                 return false;
679         }
680         ret = pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK);
681         if (ret != 0) {
682                 goto cleanup_ma;
683         }
684         ret = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED);
685         if (ret != 0) {
686                 goto cleanup_ma;
687         }
688         ret = pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST);
689         if (ret != 0) {
690                 goto cleanup_ma;
691         }
692         ret = pthread_mutex_init(&m, &ma);
693         if (ret != 0) {
694                 goto cleanup_ma;
695         }
696         ret = pthread_mutex_lock(&m);
697         if (ret != 0) {
698                 goto cleanup_m;
699         }
700         /*
701          * This makes sure we have real mutexes
702          * from a threading library instead of just
703          * stubs from libc.
704          */
705         ret = pthread_mutex_lock(&m);
706         if (ret != EDEADLK) {
707                 goto cleanup_lock;
708         }
709         ret = pthread_mutex_unlock(&m);
710         if (ret != 0) {
711                 goto cleanup_m;
712         }
713
714         tdb_mutex_locking_cached = true;
715         goto cleanup_m;
716
717 cleanup_lock:
718         pthread_mutex_unlock(&m);
719 cleanup_m:
720         pthread_mutex_destroy(&m);
721 cleanup_ma:
722         pthread_mutexattr_destroy(&ma);
723         return tdb_mutex_locking_cached;
724 }
725
726 static void (*tdb_robust_mutext_old_handler)(int) = SIG_ERR;
727 static pid_t tdb_robust_mutex_pid = -1;
728
729 static bool tdb_robust_mutex_setup_sigchild(void (*handler)(int),
730                         void (**p_old_handler)(int))
731 {
732 #ifdef HAVE_SIGACTION
733         struct sigaction act;
734         struct sigaction oldact;
735
736         memset(&act, '\0', sizeof(act));
737
738         act.sa_handler = handler;
739 #ifdef SA_RESTART
740         act.sa_flags = SA_RESTART;
741 #endif
742         sigemptyset(&act.sa_mask);
743         sigaddset(&act.sa_mask, SIGCHLD);
744         sigaction(SIGCHLD, &act, &oldact);
745         if (p_old_handler) {
746                 *p_old_handler = oldact.sa_handler;
747         }
748         return true;
749 #else /* !HAVE_SIGACTION */
750         return false;
751 #endif
752 }
753
754 static void tdb_robust_mutex_handler(int sig)
755 {
756         pid_t child_pid = tdb_robust_mutex_pid;
757
758         if (child_pid != -1) {
759                 pid_t pid;
760
761                 pid = waitpid(child_pid, NULL, WNOHANG);
762                 if (pid == -1) {
763                         switch (errno) {
764                         case ECHILD:
765                                 tdb_robust_mutex_pid = -1;
766                                 return;
767
768                         default:
769                                 return;
770                         }
771                 }
772                 if (pid == child_pid) {
773                         tdb_robust_mutex_pid = -1;
774                         return;
775                 }
776         }
777
778         if (tdb_robust_mutext_old_handler == SIG_DFL) {
779                 return;
780         }
781         if (tdb_robust_mutext_old_handler == SIG_IGN) {
782                 return;
783         }
784         if (tdb_robust_mutext_old_handler == SIG_ERR) {
785                 return;
786         }
787
788         tdb_robust_mutext_old_handler(sig);
789 }
790
791 static void tdb_robust_mutex_wait_for_child(pid_t *child_pid)
792 {
793         int options = WNOHANG;
794
795         if (*child_pid == -1) {
796                 return;
797         }
798
799         while (tdb_robust_mutex_pid > 0) {
800                 pid_t pid;
801
802                 /*
803                  * First we try with WNOHANG, as the process might not exist
804                  * anymore. Once we've sent SIGKILL we block waiting for the
805                  * exit.
806                  */
807                 pid = waitpid(*child_pid, NULL, options);
808                 if (pid == -1) {
809                         if (errno == EINTR) {
810                                 continue;
811                         } else if (errno == ECHILD) {
812                                 break;
813                         } else {
814                                 abort();
815                         }
816                 }
817                 if (pid == *child_pid) {
818                         break;
819                 }
820
821                 kill(*child_pid, SIGKILL);
822                 options = 0;
823         }
824
825         tdb_robust_mutex_pid = -1;
826         *child_pid = -1;
827 }
828
829 _PUBLIC_ bool tdb_runtime_check_for_robust_mutexes(void)
830 {
831         void *ptr = NULL;
832         pthread_mutex_t *m = NULL;
833         pthread_mutexattr_t ma;
834         int ret = 1;
835         int pipe_down[2] = { -1, -1 };
836         int pipe_up[2] = { -1, -1 };
837         ssize_t nread;
838         char c = 0;
839         bool ok;
840         static bool initialized;
841         pid_t saved_child_pid = -1;
842         bool cleanup_ma = false;
843
844         if (initialized) {
845                 return tdb_mutex_locking_cached;
846         }
847
848         initialized = true;
849
850         ok = tdb_mutex_locking_supported();
851         if (!ok) {
852                 return false;
853         }
854
855         tdb_mutex_locking_cached = false;
856
857         ptr = mmap(NULL, sizeof(pthread_mutex_t), PROT_READ|PROT_WRITE,
858                    MAP_SHARED|MAP_ANON, -1 /* fd */, 0);
859         if (ptr == MAP_FAILED) {
860                 return false;
861         }
862
863         ret = pipe(pipe_down);
864         if (ret != 0) {
865                 goto cleanup;
866         }
867         ret = pipe(pipe_up);
868         if (ret != 0) {
869                 goto cleanup;
870         }
871
872         ret = pthread_mutexattr_init(&ma);
873         if (ret != 0) {
874                 goto cleanup;
875         }
876         cleanup_ma = true;
877         ret = pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK);
878         if (ret != 0) {
879                 goto cleanup;
880         }
881         ret = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED);
882         if (ret != 0) {
883                 goto cleanup;
884         }
885         ret = pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST);
886         if (ret != 0) {
887                 goto cleanup;
888         }
889         ret = pthread_mutex_init(ptr, &ma);
890         if (ret != 0) {
891                 goto cleanup;
892         }
893         m = (pthread_mutex_t *)ptr;
894
895         if (tdb_robust_mutex_setup_sigchild(tdb_robust_mutex_handler,
896                         &tdb_robust_mutext_old_handler) == false) {
897                 goto cleanup;
898         }
899
900         tdb_robust_mutex_pid = fork();
901         saved_child_pid = tdb_robust_mutex_pid;
902         if (tdb_robust_mutex_pid == 0) {
903                 size_t nwritten;
904                 close(pipe_down[1]);
905                 close(pipe_up[0]);
906                 ret = pthread_mutex_lock(m);
907                 nwritten = write(pipe_up[1], &ret, sizeof(ret));
908                 if (nwritten != sizeof(ret)) {
909                         _exit(1);
910                 }
911                 if (ret != 0) {
912                         _exit(1);
913                 }
914                 nread = read(pipe_down[0], &c, 1);
915                 if (nread != 1) {
916                         _exit(1);
917                 }
918                 /* leave locked */
919                 _exit(0);
920         }
921         if (tdb_robust_mutex_pid == -1) {
922                 goto cleanup;
923         }
924         close(pipe_down[0]);
925         pipe_down[0] = -1;
926         close(pipe_up[1]);
927         pipe_up[1] = -1;
928
929         nread = read(pipe_up[0], &ret, sizeof(ret));
930         if (nread != sizeof(ret)) {
931                 goto cleanup;
932         }
933
934         ret = pthread_mutex_trylock(m);
935         if (ret != EBUSY) {
936                 if (ret == 0) {
937                         pthread_mutex_unlock(m);
938                 }
939                 goto cleanup;
940         }
941
942         if (write(pipe_down[1], &c, 1) != 1) {
943                 goto cleanup;
944         }
945
946         nread = read(pipe_up[0], &c, 1);
947         if (nread != 0) {
948                 goto cleanup;
949         }
950
951         tdb_robust_mutex_wait_for_child(&saved_child_pid);
952
953         ret = pthread_mutex_trylock(m);
954         if (ret != EOWNERDEAD) {
955                 if (ret == 0) {
956                         pthread_mutex_unlock(m);
957                 }
958                 goto cleanup;
959         }
960
961         ret = pthread_mutex_consistent(m);
962         if (ret != 0) {
963                 goto cleanup;
964         }
965
966         ret = pthread_mutex_trylock(m);
967         if (ret != EDEADLK && ret != EBUSY) {
968                 pthread_mutex_unlock(m);
969                 goto cleanup;
970         }
971
972         ret = pthread_mutex_unlock(m);
973         if (ret != 0) {
974                 goto cleanup;
975         }
976
977         tdb_mutex_locking_cached = true;
978
979 cleanup:
980         /*
981          * Note that we don't reset the signal handler we just reset
982          * tdb_robust_mutex_pid to -1. This is ok as this code path is only
983          * called once per process.
984          *
985          * Leaving our signal handler avoids races with other threads potentialy
986          * setting up their SIGCHLD handlers.
987          *
988          * The worst thing that can happen is that the other newer signal
989          * handler will get the SIGCHLD signal for our child and/or reap the
990          * child with a wait() function. tdb_robust_mutex_wait_for_child()
991          * handles the case where waitpid returns ECHILD.
992          */
993         tdb_robust_mutex_wait_for_child(&saved_child_pid);
994
995         if (m != NULL) {
996                 pthread_mutex_destroy(m);
997         }
998         if (cleanup_ma) {
999                 pthread_mutexattr_destroy(&ma);
1000         }
1001         if (pipe_down[0] != -1) {
1002                 close(pipe_down[0]);
1003         }
1004         if (pipe_down[1] != -1) {
1005                 close(pipe_down[1]);
1006         }
1007         if (pipe_up[0] != -1) {
1008                 close(pipe_up[0]);
1009         }
1010         if (pipe_up[1] != -1) {
1011                 close(pipe_up[1]);
1012         }
1013         if (ptr != NULL) {
1014                 munmap(ptr, sizeof(pthread_mutex_t));
1015         }
1016
1017         return tdb_mutex_locking_cached;
1018 }
1019
1020 #else
1021
1022 size_t tdb_mutex_size(struct tdb_context *tdb)
1023 {
1024         return 0;
1025 }
1026
1027 bool tdb_have_mutexes(struct tdb_context *tdb)
1028 {
1029         return false;
1030 }
1031
1032 int tdb_mutex_allrecord_lock(struct tdb_context *tdb, int ltype,
1033                              enum tdb_lock_flags flags)
1034 {
1035         tdb->ecode = TDB_ERR_LOCK;
1036         return -1;
1037 }
1038
1039 int tdb_mutex_allrecord_unlock(struct tdb_context *tdb)
1040 {
1041         return -1;
1042 }
1043
1044 int tdb_mutex_allrecord_upgrade(struct tdb_context *tdb)
1045 {
1046         tdb->ecode = TDB_ERR_LOCK;
1047         return -1;
1048 }
1049
1050 void tdb_mutex_allrecord_downgrade(struct tdb_context *tdb)
1051 {
1052         return;
1053 }
1054
1055 int tdb_mutex_mmap(struct tdb_context *tdb)
1056 {
1057         errno = ENOSYS;
1058         return -1;
1059 }
1060
1061 int tdb_mutex_munmap(struct tdb_context *tdb)
1062 {
1063         errno = ENOSYS;
1064         return -1;
1065 }
1066
1067 int tdb_mutex_init(struct tdb_context *tdb)
1068 {
1069         errno = ENOSYS;
1070         return -1;
1071 }
1072
1073 _PUBLIC_ bool tdb_runtime_check_for_robust_mutexes(void)
1074 {
1075         return false;
1076 }
1077
1078 #endif