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