proc_mutex.c revision 269847
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "apr.h"
18#include "apr_strings.h"
19#include "apr_arch_proc_mutex.h"
20#include "apr_arch_file_io.h" /* for apr_mkstemp() */
21#include "apr_hash.h"
22
23APR_DECLARE(apr_status_t) apr_proc_mutex_destroy(apr_proc_mutex_t *mutex)
24{
25    return apr_pool_cleanup_run(mutex->pool, mutex, apr_proc_mutex_cleanup);
26}
27
28#if APR_HAS_POSIXSEM_SERIALIZE || APR_HAS_FCNTL_SERIALIZE || \
29    APR_HAS_PROC_PTHREAD_SERIALIZE || APR_HAS_SYSVSEM_SERIALIZE
30static apr_status_t proc_mutex_no_child_init(apr_proc_mutex_t **mutex,
31                                             apr_pool_t *cont,
32                                             const char *fname)
33{
34    return APR_SUCCESS;
35}
36#endif
37
38#if APR_HAS_POSIXSEM_SERIALIZE
39
40#ifndef SEM_FAILED
41#define SEM_FAILED (-1)
42#endif
43
44static apr_status_t proc_mutex_posix_cleanup(void *mutex_)
45{
46    apr_proc_mutex_t *mutex = mutex_;
47
48    if (sem_close(mutex->psem_interproc) < 0) {
49        return errno;
50    }
51
52    return APR_SUCCESS;
53}
54
55static unsigned int rshash (char *p) {
56    /* hash function from Robert Sedgwicks 'Algorithms in C' book */
57   unsigned int b    = 378551;
58   unsigned int a    = 63689;
59   unsigned int retval = 0;
60
61   for( ; *p; p++)
62   {
63      retval = retval * a + (*p);
64      a *= b;
65   }
66
67   return retval;
68}
69
70static apr_status_t proc_mutex_posix_create(apr_proc_mutex_t *new_mutex,
71                                            const char *fname)
72{
73    #define APR_POSIXSEM_NAME_MIN 13
74    sem_t *psem;
75    char semname[32];
76
77    new_mutex->interproc = apr_palloc(new_mutex->pool,
78                                      sizeof(*new_mutex->interproc));
79    /*
80     * This bogusness is to follow what appears to be the
81     * lowest common denominator in Posix semaphore naming:
82     *   - start with '/'
83     *   - be at most 14 chars
84     *   - be unique and not match anything on the filesystem
85     *
86     * Because of this, we use fname to generate a (unique) hash
87     * and use that as the name of the semaphore. If no filename was
88     * given, we create one based on the time. We tuck the name
89     * away, since it might be useful for debugging. We use 2 hashing
90     * functions to try to avoid collisions.
91     *
92     * To  make this as robust as possible, we initially try something
93     * larger (and hopefully more unique) and gracefully fail down to the
94     * LCD above.
95     *
96     * NOTE: Darwin (Mac OS X) seems to be the most restrictive
97     * implementation. Versions previous to Darwin 6.2 had the 14
98     * char limit, but later rev's allow up to 31 characters.
99     *
100     */
101    if (fname) {
102        apr_ssize_t flen = strlen(fname);
103        char *p = apr_pstrndup(new_mutex->pool, fname, strlen(fname));
104        unsigned int h1, h2;
105        h1 = (apr_hashfunc_default((const char *)p, &flen) & 0xffffffff);
106        h2 = (rshash(p) & 0xffffffff);
107        apr_snprintf(semname, sizeof(semname), "/ApR.%xH%x", h1, h2);
108    } else {
109        apr_time_t now;
110        unsigned long sec;
111        unsigned long usec;
112        now = apr_time_now();
113        sec = apr_time_sec(now);
114        usec = apr_time_usec(now);
115        apr_snprintf(semname, sizeof(semname), "/ApR.%lxZ%lx", sec, usec);
116    }
117    psem = sem_open(semname, O_CREAT | O_EXCL, 0644, 1);
118    if (psem == (sem_t *)SEM_FAILED) {
119        if (errno == ENAMETOOLONG) {
120            /* Oh well, good try */
121            semname[APR_POSIXSEM_NAME_MIN] = '\0';
122        } else {
123            return errno;
124        }
125        psem = sem_open(semname, O_CREAT | O_EXCL, 0644, 1);
126    }
127
128    if (psem == (sem_t *)SEM_FAILED) {
129        return errno;
130    }
131    /* Ahhh. The joys of Posix sems. Predelete it... */
132    sem_unlink(semname);
133    new_mutex->psem_interproc = psem;
134    new_mutex->fname = apr_pstrdup(new_mutex->pool, semname);
135    apr_pool_cleanup_register(new_mutex->pool, (void *)new_mutex,
136                              apr_proc_mutex_cleanup,
137                              apr_pool_cleanup_null);
138    return APR_SUCCESS;
139}
140
141static apr_status_t proc_mutex_posix_acquire(apr_proc_mutex_t *mutex)
142{
143    if (sem_wait(mutex->psem_interproc) < 0) {
144        return errno;
145    }
146    mutex->curr_locked = 1;
147    return APR_SUCCESS;
148}
149
150static apr_status_t proc_mutex_posix_tryacquire(apr_proc_mutex_t *mutex)
151{
152    if (sem_trywait(mutex->psem_interproc) < 0) {
153        if (errno == EAGAIN) {
154            return APR_EBUSY;
155        }
156        return errno;
157    }
158    mutex->curr_locked = 1;
159    return APR_SUCCESS;
160}
161
162static apr_status_t proc_mutex_posix_release(apr_proc_mutex_t *mutex)
163{
164    mutex->curr_locked = 0;
165    if (sem_post(mutex->psem_interproc) < 0) {
166        /* any failure is probably fatal, so no big deal to leave
167         * ->curr_locked at 0. */
168        return errno;
169    }
170    return APR_SUCCESS;
171}
172
173static const apr_proc_mutex_unix_lock_methods_t mutex_posixsem_methods =
174{
175#if APR_PROCESS_LOCK_IS_GLOBAL || !APR_HAS_THREADS || defined(POSIXSEM_IS_GLOBAL)
176    APR_PROCESS_LOCK_MECH_IS_GLOBAL,
177#else
178    0,
179#endif
180    proc_mutex_posix_create,
181    proc_mutex_posix_acquire,
182    proc_mutex_posix_tryacquire,
183    proc_mutex_posix_release,
184    proc_mutex_posix_cleanup,
185    proc_mutex_no_child_init,
186    "posixsem"
187};
188
189#endif /* Posix sem implementation */
190
191#if APR_HAS_SYSVSEM_SERIALIZE
192
193static struct sembuf proc_mutex_op_on;
194static struct sembuf proc_mutex_op_try;
195static struct sembuf proc_mutex_op_off;
196
197static void proc_mutex_sysv_setup(void)
198{
199    proc_mutex_op_on.sem_num = 0;
200    proc_mutex_op_on.sem_op = -1;
201    proc_mutex_op_on.sem_flg = SEM_UNDO;
202    proc_mutex_op_try.sem_num = 0;
203    proc_mutex_op_try.sem_op = -1;
204    proc_mutex_op_try.sem_flg = SEM_UNDO | IPC_NOWAIT;
205    proc_mutex_op_off.sem_num = 0;
206    proc_mutex_op_off.sem_op = 1;
207    proc_mutex_op_off.sem_flg = SEM_UNDO;
208}
209
210static apr_status_t proc_mutex_sysv_cleanup(void *mutex_)
211{
212    apr_proc_mutex_t *mutex=mutex_;
213    union semun ick;
214
215    if (mutex->interproc->filedes != -1) {
216        ick.val = 0;
217        semctl(mutex->interproc->filedes, 0, IPC_RMID, ick);
218    }
219    return APR_SUCCESS;
220}
221
222static apr_status_t proc_mutex_sysv_create(apr_proc_mutex_t *new_mutex,
223                                           const char *fname)
224{
225    union semun ick;
226    apr_status_t rv;
227
228    new_mutex->interproc = apr_palloc(new_mutex->pool, sizeof(*new_mutex->interproc));
229    new_mutex->interproc->filedes = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
230
231    if (new_mutex->interproc->filedes < 0) {
232        rv = errno;
233        proc_mutex_sysv_cleanup(new_mutex);
234        return rv;
235    }
236    ick.val = 1;
237    if (semctl(new_mutex->interproc->filedes, 0, SETVAL, ick) < 0) {
238        rv = errno;
239        proc_mutex_sysv_cleanup(new_mutex);
240        return rv;
241    }
242    new_mutex->curr_locked = 0;
243    apr_pool_cleanup_register(new_mutex->pool,
244                              (void *)new_mutex, apr_proc_mutex_cleanup,
245                              apr_pool_cleanup_null);
246    return APR_SUCCESS;
247}
248
249static apr_status_t proc_mutex_sysv_acquire(apr_proc_mutex_t *mutex)
250{
251    int rc;
252
253    do {
254        rc = semop(mutex->interproc->filedes, &proc_mutex_op_on, 1);
255    } while (rc < 0 && errno == EINTR);
256    if (rc < 0) {
257        return errno;
258    }
259    mutex->curr_locked = 1;
260    return APR_SUCCESS;
261}
262
263static apr_status_t proc_mutex_sysv_tryacquire(apr_proc_mutex_t *mutex)
264{
265    int rc;
266
267    do {
268        rc = semop(mutex->interproc->filedes, &proc_mutex_op_try, 1);
269    } while (rc < 0 && errno == EINTR);
270    if (rc < 0) {
271        if (errno == EAGAIN) {
272            return APR_EBUSY;
273        }
274        return errno;
275    }
276    mutex->curr_locked = 1;
277    return APR_SUCCESS;
278}
279
280static apr_status_t proc_mutex_sysv_release(apr_proc_mutex_t *mutex)
281{
282    int rc;
283
284    mutex->curr_locked = 0;
285    do {
286        rc = semop(mutex->interproc->filedes, &proc_mutex_op_off, 1);
287    } while (rc < 0 && errno == EINTR);
288    if (rc < 0) {
289        return errno;
290    }
291    return APR_SUCCESS;
292}
293
294static const apr_proc_mutex_unix_lock_methods_t mutex_sysv_methods =
295{
296#if APR_PROCESS_LOCK_IS_GLOBAL || !APR_HAS_THREADS || defined(SYSVSEM_IS_GLOBAL)
297    APR_PROCESS_LOCK_MECH_IS_GLOBAL,
298#else
299    0,
300#endif
301    proc_mutex_sysv_create,
302    proc_mutex_sysv_acquire,
303    proc_mutex_sysv_tryacquire,
304    proc_mutex_sysv_release,
305    proc_mutex_sysv_cleanup,
306    proc_mutex_no_child_init,
307    "sysvsem"
308};
309
310#endif /* SysV sem implementation */
311
312#if APR_HAS_PROC_PTHREAD_SERIALIZE
313
314static apr_status_t proc_mutex_proc_pthread_cleanup(void *mutex_)
315{
316    apr_proc_mutex_t *mutex=mutex_;
317    apr_status_t rv;
318
319    if (mutex->curr_locked == 1) {
320        if ((rv = pthread_mutex_unlock(mutex->pthread_interproc))) {
321#ifdef HAVE_ZOS_PTHREADS
322            rv = errno;
323#endif
324            return rv;
325        }
326    }
327    /* curr_locked is set to -1 until the mutex has been created */
328    if (mutex->curr_locked != -1) {
329        if ((rv = pthread_mutex_destroy(mutex->pthread_interproc))) {
330#ifdef HAVE_ZOS_PTHREADS
331            rv = errno;
332#endif
333            return rv;
334        }
335    }
336    if (munmap((caddr_t)mutex->pthread_interproc, sizeof(pthread_mutex_t))) {
337        return errno;
338    }
339    return APR_SUCCESS;
340}
341
342static apr_status_t proc_mutex_proc_pthread_create(apr_proc_mutex_t *new_mutex,
343                                                   const char *fname)
344{
345    apr_status_t rv;
346    int fd;
347    pthread_mutexattr_t mattr;
348
349    fd = open("/dev/zero", O_RDWR);
350    if (fd < 0) {
351        return errno;
352    }
353
354    new_mutex->pthread_interproc = (pthread_mutex_t *)mmap(
355                                       (caddr_t) 0,
356                                       sizeof(pthread_mutex_t),
357                                       PROT_READ | PROT_WRITE, MAP_SHARED,
358                                       fd, 0);
359    if (new_mutex->pthread_interproc == (pthread_mutex_t *) (caddr_t) -1) {
360        close(fd);
361        return errno;
362    }
363    close(fd);
364
365    new_mutex->curr_locked = -1; /* until the mutex has been created */
366
367    if ((rv = pthread_mutexattr_init(&mattr))) {
368#ifdef HAVE_ZOS_PTHREADS
369        rv = errno;
370#endif
371        proc_mutex_proc_pthread_cleanup(new_mutex);
372        return rv;
373    }
374    if ((rv = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED))) {
375#ifdef HAVE_ZOS_PTHREADS
376        rv = errno;
377#endif
378        proc_mutex_proc_pthread_cleanup(new_mutex);
379        pthread_mutexattr_destroy(&mattr);
380        return rv;
381    }
382
383#ifdef HAVE_PTHREAD_MUTEX_ROBUST
384    if ((rv = pthread_mutexattr_setrobust_np(&mattr,
385                                               PTHREAD_MUTEX_ROBUST_NP))) {
386#ifdef HAVE_ZOS_PTHREADS
387        rv = errno;
388#endif
389        proc_mutex_proc_pthread_cleanup(new_mutex);
390        pthread_mutexattr_destroy(&mattr);
391        return rv;
392    }
393    if ((rv = pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT))) {
394#ifdef HAVE_ZOS_PTHREADS
395        rv = errno;
396#endif
397        proc_mutex_proc_pthread_cleanup(new_mutex);
398        pthread_mutexattr_destroy(&mattr);
399        return rv;
400    }
401#endif /* HAVE_PTHREAD_MUTEX_ROBUST */
402
403    if ((rv = pthread_mutex_init(new_mutex->pthread_interproc, &mattr))) {
404#ifdef HAVE_ZOS_PTHREADS
405        rv = errno;
406#endif
407        proc_mutex_proc_pthread_cleanup(new_mutex);
408        pthread_mutexattr_destroy(&mattr);
409        return rv;
410    }
411
412    new_mutex->curr_locked = 0; /* mutex created now */
413
414    if ((rv = pthread_mutexattr_destroy(&mattr))) {
415#ifdef HAVE_ZOS_PTHREADS
416        rv = errno;
417#endif
418        proc_mutex_proc_pthread_cleanup(new_mutex);
419        return rv;
420    }
421
422    apr_pool_cleanup_register(new_mutex->pool,
423                              (void *)new_mutex,
424                              apr_proc_mutex_cleanup,
425                              apr_pool_cleanup_null);
426    return APR_SUCCESS;
427}
428
429static apr_status_t proc_mutex_proc_pthread_acquire(apr_proc_mutex_t *mutex)
430{
431    apr_status_t rv;
432
433    if ((rv = pthread_mutex_lock(mutex->pthread_interproc))) {
434#ifdef HAVE_ZOS_PTHREADS
435        rv = errno;
436#endif
437#ifdef HAVE_PTHREAD_MUTEX_ROBUST
438        /* Okay, our owner died.  Let's try to make it consistent again. */
439        if (rv == EOWNERDEAD) {
440            pthread_mutex_consistent_np(mutex->pthread_interproc);
441        }
442        else
443            return rv;
444#else
445        return rv;
446#endif
447    }
448    mutex->curr_locked = 1;
449    return APR_SUCCESS;
450}
451
452static apr_status_t proc_mutex_proc_pthread_tryacquire(apr_proc_mutex_t *mutex)
453{
454    apr_status_t rv;
455
456    if ((rv = pthread_mutex_trylock(mutex->pthread_interproc))) {
457#ifdef HAVE_ZOS_PTHREADS
458        rv = errno;
459#endif
460        if (rv == EBUSY) {
461            return APR_EBUSY;
462        }
463#ifdef HAVE_PTHREAD_MUTEX_ROBUST
464        /* Okay, our owner died.  Let's try to make it consistent again. */
465        if (rv == EOWNERDEAD) {
466            pthread_mutex_consistent_np(mutex->pthread_interproc);
467            rv = APR_SUCCESS;
468        }
469        else
470            return rv;
471#else
472        return rv;
473#endif
474    }
475    mutex->curr_locked = 1;
476    return rv;
477}
478
479static apr_status_t proc_mutex_proc_pthread_release(apr_proc_mutex_t *mutex)
480{
481    apr_status_t rv;
482
483    mutex->curr_locked = 0;
484    if ((rv = pthread_mutex_unlock(mutex->pthread_interproc))) {
485#ifdef HAVE_ZOS_PTHREADS
486        rv = errno;
487#endif
488        return rv;
489    }
490    return APR_SUCCESS;
491}
492
493static const apr_proc_mutex_unix_lock_methods_t mutex_proc_pthread_methods =
494{
495    APR_PROCESS_LOCK_MECH_IS_GLOBAL,
496    proc_mutex_proc_pthread_create,
497    proc_mutex_proc_pthread_acquire,
498    proc_mutex_proc_pthread_tryacquire,
499    proc_mutex_proc_pthread_release,
500    proc_mutex_proc_pthread_cleanup,
501    proc_mutex_no_child_init,
502    "pthread"
503};
504
505#endif
506
507#if APR_HAS_FCNTL_SERIALIZE
508
509static struct flock proc_mutex_lock_it;
510static struct flock proc_mutex_unlock_it;
511
512static apr_status_t proc_mutex_fcntl_release(apr_proc_mutex_t *);
513
514static void proc_mutex_fcntl_setup(void)
515{
516    proc_mutex_lock_it.l_whence = SEEK_SET;   /* from current point */
517    proc_mutex_lock_it.l_start = 0;           /* -"- */
518    proc_mutex_lock_it.l_len = 0;             /* until end of file */
519    proc_mutex_lock_it.l_type = F_WRLCK;      /* set exclusive/write lock */
520    proc_mutex_lock_it.l_pid = 0;             /* pid not actually interesting */
521    proc_mutex_unlock_it.l_whence = SEEK_SET; /* from current point */
522    proc_mutex_unlock_it.l_start = 0;         /* -"- */
523    proc_mutex_unlock_it.l_len = 0;           /* until end of file */
524    proc_mutex_unlock_it.l_type = F_UNLCK;    /* set exclusive/write lock */
525    proc_mutex_unlock_it.l_pid = 0;           /* pid not actually interesting */
526}
527
528static apr_status_t proc_mutex_fcntl_cleanup(void *mutex_)
529{
530    apr_status_t status;
531    apr_proc_mutex_t *mutex=mutex_;
532
533    if (mutex->curr_locked == 1) {
534        status = proc_mutex_fcntl_release(mutex);
535        if (status != APR_SUCCESS)
536            return status;
537    }
538
539    return apr_file_close(mutex->interproc);
540}
541
542static apr_status_t proc_mutex_fcntl_create(apr_proc_mutex_t *new_mutex,
543                                            const char *fname)
544{
545    int rv;
546
547    if (fname) {
548        new_mutex->fname = apr_pstrdup(new_mutex->pool, fname);
549        rv = apr_file_open(&new_mutex->interproc, new_mutex->fname,
550                           APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_EXCL,
551                           APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD,
552                           new_mutex->pool);
553    }
554    else {
555        new_mutex->fname = apr_pstrdup(new_mutex->pool, "/tmp/aprXXXXXX");
556        rv = apr_file_mktemp(&new_mutex->interproc, new_mutex->fname,
557                             APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_EXCL,
558                             new_mutex->pool);
559    }
560
561    if (rv != APR_SUCCESS) {
562        return rv;
563    }
564
565    new_mutex->curr_locked = 0;
566    unlink(new_mutex->fname);
567    apr_pool_cleanup_register(new_mutex->pool,
568                              (void*)new_mutex,
569                              apr_proc_mutex_cleanup,
570                              apr_pool_cleanup_null);
571    return APR_SUCCESS;
572}
573
574static apr_status_t proc_mutex_fcntl_acquire(apr_proc_mutex_t *mutex)
575{
576    int rc;
577
578    do {
579        rc = fcntl(mutex->interproc->filedes, F_SETLKW, &proc_mutex_lock_it);
580    } while (rc < 0 && errno == EINTR);
581    if (rc < 0) {
582        return errno;
583    }
584    mutex->curr_locked=1;
585    return APR_SUCCESS;
586}
587
588static apr_status_t proc_mutex_fcntl_tryacquire(apr_proc_mutex_t *mutex)
589{
590    int rc;
591
592    do {
593        rc = fcntl(mutex->interproc->filedes, F_SETLK, &proc_mutex_lock_it);
594    } while (rc < 0 && errno == EINTR);
595    if (rc < 0) {
596#if FCNTL_TRYACQUIRE_EACCES
597        if (errno == EACCES) {
598#else
599        if (errno == EAGAIN) {
600#endif
601            return APR_EBUSY;
602        }
603        return errno;
604    }
605    mutex->curr_locked = 1;
606    return APR_SUCCESS;
607}
608
609static apr_status_t proc_mutex_fcntl_release(apr_proc_mutex_t *mutex)
610{
611    int rc;
612
613    mutex->curr_locked=0;
614    do {
615        rc = fcntl(mutex->interproc->filedes, F_SETLKW, &proc_mutex_unlock_it);
616    } while (rc < 0 && errno == EINTR);
617    if (rc < 0) {
618        return errno;
619    }
620    return APR_SUCCESS;
621}
622
623static const apr_proc_mutex_unix_lock_methods_t mutex_fcntl_methods =
624{
625#if APR_PROCESS_LOCK_IS_GLOBAL || !APR_HAS_THREADS || defined(FCNTL_IS_GLOBAL)
626    APR_PROCESS_LOCK_MECH_IS_GLOBAL,
627#else
628    0,
629#endif
630    proc_mutex_fcntl_create,
631    proc_mutex_fcntl_acquire,
632    proc_mutex_fcntl_tryacquire,
633    proc_mutex_fcntl_release,
634    proc_mutex_fcntl_cleanup,
635    proc_mutex_no_child_init,
636    "fcntl"
637};
638
639#endif /* fcntl implementation */
640
641#if APR_HAS_FLOCK_SERIALIZE
642
643static apr_status_t proc_mutex_flock_release(apr_proc_mutex_t *);
644
645static apr_status_t proc_mutex_flock_cleanup(void *mutex_)
646{
647    apr_status_t status;
648    apr_proc_mutex_t *mutex=mutex_;
649
650    if (mutex->curr_locked == 1) {
651        status = proc_mutex_flock_release(mutex);
652        if (status != APR_SUCCESS)
653            return status;
654    }
655    if (mutex->interproc) { /* if it was opened properly */
656        apr_file_close(mutex->interproc);
657    }
658    unlink(mutex->fname);
659    return APR_SUCCESS;
660}
661
662static apr_status_t proc_mutex_flock_create(apr_proc_mutex_t *new_mutex,
663                                            const char *fname)
664{
665    int rv;
666
667    if (fname) {
668        new_mutex->fname = apr_pstrdup(new_mutex->pool, fname);
669        rv = apr_file_open(&new_mutex->interproc, new_mutex->fname,
670                           APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_EXCL,
671                           APR_UREAD | APR_UWRITE,
672                           new_mutex->pool);
673    }
674    else {
675        new_mutex->fname = apr_pstrdup(new_mutex->pool, "/tmp/aprXXXXXX");
676        rv = apr_file_mktemp(&new_mutex->interproc, new_mutex->fname,
677                             APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_EXCL,
678                             new_mutex->pool);
679    }
680
681    if (rv != APR_SUCCESS) {
682        proc_mutex_flock_cleanup(new_mutex);
683        return errno;
684    }
685    new_mutex->curr_locked = 0;
686    apr_pool_cleanup_register(new_mutex->pool, (void *)new_mutex,
687                              apr_proc_mutex_cleanup,
688                              apr_pool_cleanup_null);
689    return APR_SUCCESS;
690}
691
692static apr_status_t proc_mutex_flock_acquire(apr_proc_mutex_t *mutex)
693{
694    int rc;
695
696    do {
697        rc = flock(mutex->interproc->filedes, LOCK_EX);
698    } while (rc < 0 && errno == EINTR);
699    if (rc < 0) {
700        return errno;
701    }
702    mutex->curr_locked = 1;
703    return APR_SUCCESS;
704}
705
706static apr_status_t proc_mutex_flock_tryacquire(apr_proc_mutex_t *mutex)
707{
708    int rc;
709
710    do {
711        rc = flock(mutex->interproc->filedes, LOCK_EX | LOCK_NB);
712    } while (rc < 0 && errno == EINTR);
713    if (rc < 0) {
714        if (errno == EWOULDBLOCK || errno == EAGAIN) {
715            return APR_EBUSY;
716        }
717        return errno;
718    }
719    mutex->curr_locked = 1;
720    return APR_SUCCESS;
721}
722
723static apr_status_t proc_mutex_flock_release(apr_proc_mutex_t *mutex)
724{
725    int rc;
726
727    mutex->curr_locked = 0;
728    do {
729        rc = flock(mutex->interproc->filedes, LOCK_UN);
730    } while (rc < 0 && errno == EINTR);
731    if (rc < 0) {
732        return errno;
733    }
734    return APR_SUCCESS;
735}
736
737static apr_status_t proc_mutex_flock_child_init(apr_proc_mutex_t **mutex,
738                                                apr_pool_t *pool,
739                                                const char *fname)
740{
741    apr_proc_mutex_t *new_mutex;
742    int rv;
743
744    new_mutex = (apr_proc_mutex_t *)apr_palloc(pool, sizeof(apr_proc_mutex_t));
745
746    memcpy(new_mutex, *mutex, sizeof *new_mutex);
747    new_mutex->pool = pool;
748    if (!fname) {
749        fname = (*mutex)->fname;
750    }
751    new_mutex->fname = apr_pstrdup(pool, fname);
752    rv = apr_file_open(&new_mutex->interproc, new_mutex->fname,
753                       APR_FOPEN_WRITE, 0, new_mutex->pool);
754    if (rv != APR_SUCCESS) {
755        return rv;
756    }
757    *mutex = new_mutex;
758    return APR_SUCCESS;
759}
760
761static const apr_proc_mutex_unix_lock_methods_t mutex_flock_methods =
762{
763#if APR_PROCESS_LOCK_IS_GLOBAL || !APR_HAS_THREADS || defined(FLOCK_IS_GLOBAL)
764    APR_PROCESS_LOCK_MECH_IS_GLOBAL,
765#else
766    0,
767#endif
768    proc_mutex_flock_create,
769    proc_mutex_flock_acquire,
770    proc_mutex_flock_tryacquire,
771    proc_mutex_flock_release,
772    proc_mutex_flock_cleanup,
773    proc_mutex_flock_child_init,
774    "flock"
775};
776
777#endif /* flock implementation */
778
779void apr_proc_mutex_unix_setup_lock(void)
780{
781    /* setup only needed for sysvsem and fnctl */
782#if APR_HAS_SYSVSEM_SERIALIZE
783    proc_mutex_sysv_setup();
784#endif
785#if APR_HAS_FCNTL_SERIALIZE
786    proc_mutex_fcntl_setup();
787#endif
788}
789
790static apr_status_t proc_mutex_choose_method(apr_proc_mutex_t *new_mutex, apr_lockmech_e mech)
791{
792    switch (mech) {
793    case APR_LOCK_FCNTL:
794#if APR_HAS_FCNTL_SERIALIZE
795        new_mutex->inter_meth = &mutex_fcntl_methods;
796#else
797        return APR_ENOTIMPL;
798#endif
799        break;
800    case APR_LOCK_FLOCK:
801#if APR_HAS_FLOCK_SERIALIZE
802        new_mutex->inter_meth = &mutex_flock_methods;
803#else
804        return APR_ENOTIMPL;
805#endif
806        break;
807    case APR_LOCK_SYSVSEM:
808#if APR_HAS_SYSVSEM_SERIALIZE
809        new_mutex->inter_meth = &mutex_sysv_methods;
810#else
811        return APR_ENOTIMPL;
812#endif
813        break;
814    case APR_LOCK_POSIXSEM:
815#if APR_HAS_POSIXSEM_SERIALIZE
816        new_mutex->inter_meth = &mutex_posixsem_methods;
817#else
818        return APR_ENOTIMPL;
819#endif
820        break;
821    case APR_LOCK_PROC_PTHREAD:
822#if APR_HAS_PROC_PTHREAD_SERIALIZE
823        new_mutex->inter_meth = &mutex_proc_pthread_methods;
824#else
825        return APR_ENOTIMPL;
826#endif
827        break;
828    case APR_LOCK_DEFAULT:
829#if APR_USE_FLOCK_SERIALIZE
830        new_mutex->inter_meth = &mutex_flock_methods;
831#elif APR_USE_SYSVSEM_SERIALIZE
832        new_mutex->inter_meth = &mutex_sysv_methods;
833#elif APR_USE_FCNTL_SERIALIZE
834        new_mutex->inter_meth = &mutex_fcntl_methods;
835#elif APR_USE_PROC_PTHREAD_SERIALIZE
836        new_mutex->inter_meth = &mutex_proc_pthread_methods;
837#elif APR_USE_POSIXSEM_SERIALIZE
838        new_mutex->inter_meth = &mutex_posixsem_methods;
839#else
840        return APR_ENOTIMPL;
841#endif
842        break;
843    default:
844        return APR_ENOTIMPL;
845    }
846    return APR_SUCCESS;
847}
848
849APR_DECLARE(const char *) apr_proc_mutex_defname(void)
850{
851    apr_status_t rv;
852    apr_proc_mutex_t mutex;
853
854    if ((rv = proc_mutex_choose_method(&mutex, APR_LOCK_DEFAULT)) != APR_SUCCESS) {
855        return "unknown";
856    }
857    mutex.meth = mutex.inter_meth;
858
859    return apr_proc_mutex_name(&mutex);
860}
861
862static apr_status_t proc_mutex_create(apr_proc_mutex_t *new_mutex, apr_lockmech_e mech, const char *fname)
863{
864    apr_status_t rv;
865
866    if ((rv = proc_mutex_choose_method(new_mutex, mech)) != APR_SUCCESS) {
867        return rv;
868    }
869
870    new_mutex->meth = new_mutex->inter_meth;
871
872    if ((rv = new_mutex->meth->create(new_mutex, fname)) != APR_SUCCESS) {
873        return rv;
874    }
875
876    return APR_SUCCESS;
877}
878
879APR_DECLARE(apr_status_t) apr_proc_mutex_create(apr_proc_mutex_t **mutex,
880                                                const char *fname,
881                                                apr_lockmech_e mech,
882                                                apr_pool_t *pool)
883{
884    apr_proc_mutex_t *new_mutex;
885    apr_status_t rv;
886
887    new_mutex = apr_pcalloc(pool, sizeof(apr_proc_mutex_t));
888    new_mutex->pool = pool;
889
890    if ((rv = proc_mutex_create(new_mutex, mech, fname)) != APR_SUCCESS)
891        return rv;
892
893    *mutex = new_mutex;
894    return APR_SUCCESS;
895}
896
897APR_DECLARE(apr_status_t) apr_proc_mutex_child_init(apr_proc_mutex_t **mutex,
898                                                    const char *fname,
899                                                    apr_pool_t *pool)
900{
901    return (*mutex)->meth->child_init(mutex, pool, fname);
902}
903
904APR_DECLARE(apr_status_t) apr_proc_mutex_lock(apr_proc_mutex_t *mutex)
905{
906    return mutex->meth->acquire(mutex);
907}
908
909APR_DECLARE(apr_status_t) apr_proc_mutex_trylock(apr_proc_mutex_t *mutex)
910{
911    return mutex->meth->tryacquire(mutex);
912}
913
914APR_DECLARE(apr_status_t) apr_proc_mutex_unlock(apr_proc_mutex_t *mutex)
915{
916    return mutex->meth->release(mutex);
917}
918
919APR_DECLARE(apr_status_t) apr_proc_mutex_cleanup(void *mutex)
920{
921    return ((apr_proc_mutex_t *)mutex)->meth->cleanup(mutex);
922}
923
924APR_DECLARE(const char *) apr_proc_mutex_name(apr_proc_mutex_t *mutex)
925{
926    return mutex->meth->name;
927}
928
929APR_DECLARE(const char *) apr_proc_mutex_lockfile(apr_proc_mutex_t *mutex)
930{
931    /* POSIX sems use the fname field but don't use a file,
932     * so be careful. */
933#if APR_HAS_FLOCK_SERIALIZE
934    if (mutex->meth == &mutex_flock_methods) {
935        return mutex->fname;
936    }
937#endif
938#if APR_HAS_FCNTL_SERIALIZE
939    if (mutex->meth == &mutex_fcntl_methods) {
940        return mutex->fname;
941    }
942#endif
943    return NULL;
944}
945
946APR_POOL_IMPLEMENT_ACCESSOR(proc_mutex)
947
948/* Implement OS-specific accessors defined in apr_portable.h */
949
950APR_DECLARE(apr_status_t) apr_os_proc_mutex_get(apr_os_proc_mutex_t *ospmutex,
951                                                apr_proc_mutex_t *pmutex)
952{
953#if APR_HAS_SYSVSEM_SERIALIZE || APR_HAS_FCNTL_SERIALIZE || APR_HAS_FLOCK_SERIALIZE || APR_HAS_POSIXSEM_SERIALIZE
954    if (pmutex->interproc) {
955        ospmutex->crossproc = pmutex->interproc->filedes;
956    }
957    else {
958        ospmutex->crossproc = -1;
959    }
960#endif
961#if APR_HAS_PROC_PTHREAD_SERIALIZE
962    ospmutex->pthread_interproc = pmutex->pthread_interproc;
963#endif
964    return APR_SUCCESS;
965}
966
967APR_DECLARE(apr_status_t) apr_os_proc_mutex_put(apr_proc_mutex_t **pmutex,
968                                                apr_os_proc_mutex_t *ospmutex,
969                                                apr_pool_t *pool)
970{
971    if (pool == NULL) {
972        return APR_ENOPOOL;
973    }
974    if ((*pmutex) == NULL) {
975        (*pmutex) = (apr_proc_mutex_t *)apr_pcalloc(pool,
976                                                    sizeof(apr_proc_mutex_t));
977        (*pmutex)->pool = pool;
978    }
979#if APR_HAS_SYSVSEM_SERIALIZE || APR_HAS_FCNTL_SERIALIZE || APR_HAS_FLOCK_SERIALIZE || APR_HAS_POSIXSEM_SERIALIZE
980    apr_os_file_put(&(*pmutex)->interproc, &ospmutex->crossproc, 0, pool);
981#endif
982#if APR_HAS_PROC_PTHREAD_SERIALIZE
983    (*pmutex)->pthread_interproc = ospmutex->pthread_interproc;
984#endif
985    return APR_SUCCESS;
986}
987
988