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 #include "apr_atomic.h"
23
apr_proc_mutex_destroy(apr_proc_mutex_t * mutex)24 APR_DECLARE(apr_status_t) apr_proc_mutex_destroy(apr_proc_mutex_t *mutex)
25 {
26 return apr_pool_cleanup_run(mutex->pool, mutex, apr_proc_mutex_cleanup);
27 }
28
29 #if APR_HAS_POSIXSEM_SERIALIZE || APR_HAS_FCNTL_SERIALIZE || \
30 APR_HAS_SYSVSEM_SERIALIZE
proc_mutex_no_child_init(apr_proc_mutex_t ** mutex,apr_pool_t * cont,const char * fname)31 static apr_status_t proc_mutex_no_child_init(apr_proc_mutex_t **mutex,
32 apr_pool_t *cont,
33 const char *fname)
34 {
35 return APR_SUCCESS;
36 }
37 #endif
38
39 #if APR_HAS_POSIXSEM_SERIALIZE || APR_HAS_PROC_PTHREAD_SERIALIZE
proc_mutex_no_perms_set(apr_proc_mutex_t * mutex,apr_fileperms_t perms,apr_uid_t uid,apr_gid_t gid)40 static apr_status_t proc_mutex_no_perms_set(apr_proc_mutex_t *mutex,
41 apr_fileperms_t perms,
42 apr_uid_t uid,
43 apr_gid_t gid)
44 {
45 return APR_ENOTIMPL;
46 }
47 #endif
48
49 #if APR_HAS_FCNTL_SERIALIZE \
50 || APR_HAS_FLOCK_SERIALIZE \
51 || (APR_HAS_SYSVSEM_SERIALIZE \
52 && !defined(HAVE_SEMTIMEDOP)) \
53 || (APR_HAS_POSIXSEM_SERIALIZE \
54 && !defined(HAVE_SEM_TIMEDWAIT)) \
55 || (APR_HAS_PROC_PTHREAD_SERIALIZE \
56 && !defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK) \
57 && !defined(HAVE_PTHREAD_CONDATTR_SETPSHARED))
proc_mutex_spinsleep_timedacquire(apr_proc_mutex_t * mutex,apr_interval_time_t timeout)58 static apr_status_t proc_mutex_spinsleep_timedacquire(apr_proc_mutex_t *mutex,
59 apr_interval_time_t timeout)
60 {
61 #define SLEEP_TIME apr_time_from_msec(10)
62 apr_status_t rv;
63 for (;;) {
64 rv = apr_proc_mutex_trylock(mutex);
65 if (!APR_STATUS_IS_EBUSY(rv)) {
66 if (rv == APR_SUCCESS) {
67 mutex->curr_locked = 1;
68 }
69 break;
70 }
71 if (timeout <= 0) {
72 rv = APR_TIMEUP;
73 break;
74 }
75 if (timeout > SLEEP_TIME) {
76 apr_sleep(SLEEP_TIME);
77 timeout -= SLEEP_TIME;
78 }
79 else {
80 apr_sleep(timeout);
81 timeout = 0;
82 }
83 }
84 return rv;
85 }
86 #endif
87
88 #if APR_HAS_POSIXSEM_SERIALIZE
89
90 #ifndef SEM_FAILED
91 #define SEM_FAILED (-1)
92 #endif
93
proc_mutex_posix_cleanup(void * mutex_)94 static apr_status_t proc_mutex_posix_cleanup(void *mutex_)
95 {
96 apr_proc_mutex_t *mutex = mutex_;
97
98 if (sem_close(mutex->os.psem_interproc) < 0) {
99 return errno;
100 }
101
102 return APR_SUCCESS;
103 }
104
rshash(char * p)105 static unsigned int rshash (char *p) {
106 /* hash function from Robert Sedgwicks 'Algorithms in C' book */
107 unsigned int b = 378551;
108 unsigned int a = 63689;
109 unsigned int retval = 0;
110
111 for( ; *p; p++)
112 {
113 retval = retval * a + (*p);
114 a *= b;
115 }
116
117 return retval;
118 }
119
proc_mutex_posix_create(apr_proc_mutex_t * new_mutex,const char * fname)120 static apr_status_t proc_mutex_posix_create(apr_proc_mutex_t *new_mutex,
121 const char *fname)
122 {
123 #define APR_POSIXSEM_NAME_MIN 13
124 sem_t *psem;
125 char semname[32];
126
127 /*
128 * This bogusness is to follow what appears to be the
129 * lowest common denominator in Posix semaphore naming:
130 * - start with '/'
131 * - be at most 14 chars
132 * - be unique and not match anything on the filesystem
133 *
134 * Because of this, we use fname to generate a (unique) hash
135 * and use that as the name of the semaphore. If no filename was
136 * given, we create one based on the time. We tuck the name
137 * away, since it might be useful for debugging. We use 2 hashing
138 * functions to try to avoid collisions.
139 *
140 * To make this as robust as possible, we initially try something
141 * larger (and hopefully more unique) and gracefully fail down to the
142 * LCD above.
143 *
144 * NOTE: Darwin (Mac OS X) seems to be the most restrictive
145 * implementation. Versions previous to Darwin 6.2 had the 14
146 * char limit, but later rev's allow up to 31 characters.
147 *
148 */
149 if (fname) {
150 apr_ssize_t flen = strlen(fname);
151 char *p = apr_pstrndup(new_mutex->pool, fname, strlen(fname));
152 unsigned int h1, h2;
153 h1 = (apr_hashfunc_default((const char *)p, &flen) & 0xffffffff);
154 h2 = (rshash(p) & 0xffffffff);
155 apr_snprintf(semname, sizeof(semname), "/ApR.%xH%x", h1, h2);
156 } else {
157 apr_time_t now;
158 unsigned long sec;
159 unsigned long usec;
160 now = apr_time_now();
161 sec = apr_time_sec(now);
162 usec = apr_time_usec(now);
163 apr_snprintf(semname, sizeof(semname), "/ApR.%lxZ%lx", sec, usec);
164 }
165 do {
166 psem = sem_open(semname, O_CREAT | O_EXCL, 0644, 1);
167 } while (psem == (sem_t *)SEM_FAILED && errno == EINTR);
168 if (psem == (sem_t *)SEM_FAILED) {
169 if (errno == ENAMETOOLONG) {
170 /* Oh well, good try */
171 semname[APR_POSIXSEM_NAME_MIN] = '\0';
172 } else {
173 return errno;
174 }
175 do {
176 psem = sem_open(semname, O_CREAT | O_EXCL, 0644, 1);
177 } while (psem == (sem_t *)SEM_FAILED && errno == EINTR);
178 }
179
180 if (psem == (sem_t *)SEM_FAILED) {
181 return errno;
182 }
183 /* Ahhh. The joys of Posix sems. Predelete it... */
184 sem_unlink(semname);
185 new_mutex->os.psem_interproc = psem;
186 new_mutex->fname = apr_pstrdup(new_mutex->pool, semname);
187 apr_pool_cleanup_register(new_mutex->pool, (void *)new_mutex,
188 apr_proc_mutex_cleanup,
189 apr_pool_cleanup_null);
190 return APR_SUCCESS;
191 }
192
proc_mutex_posix_acquire(apr_proc_mutex_t * mutex)193 static apr_status_t proc_mutex_posix_acquire(apr_proc_mutex_t *mutex)
194 {
195 int rc;
196
197 do {
198 rc = sem_wait(mutex->os.psem_interproc);
199 } while (rc < 0 && errno == EINTR);
200 if (rc < 0) {
201 return errno;
202 }
203 mutex->curr_locked = 1;
204 return APR_SUCCESS;
205 }
206
proc_mutex_posix_tryacquire(apr_proc_mutex_t * mutex)207 static apr_status_t proc_mutex_posix_tryacquire(apr_proc_mutex_t *mutex)
208 {
209 int rc;
210
211 do {
212 rc = sem_trywait(mutex->os.psem_interproc);
213 } while (rc < 0 && errno == EINTR);
214 if (rc < 0) {
215 if (errno == EAGAIN) {
216 return APR_EBUSY;
217 }
218 return errno;
219 }
220 mutex->curr_locked = 1;
221 return APR_SUCCESS;
222 }
223
224 #if defined(HAVE_SEM_TIMEDWAIT)
proc_mutex_posix_timedacquire(apr_proc_mutex_t * mutex,apr_interval_time_t timeout)225 static apr_status_t proc_mutex_posix_timedacquire(apr_proc_mutex_t *mutex,
226 apr_interval_time_t timeout)
227 {
228 if (timeout <= 0) {
229 apr_status_t rv = proc_mutex_posix_tryacquire(mutex);
230 return (rv == APR_EBUSY) ? APR_TIMEUP : rv;
231 }
232 else {
233 int rc;
234 struct timespec abstime;
235
236 timeout += apr_time_now();
237 abstime.tv_sec = apr_time_sec(timeout);
238 abstime.tv_nsec = apr_time_usec(timeout) * 1000; /* nanoseconds */
239
240 do {
241 rc = sem_timedwait(mutex->os.psem_interproc, &abstime);
242 } while (rc < 0 && errno == EINTR);
243 if (rc < 0) {
244 if (errno == ETIMEDOUT) {
245 return APR_TIMEUP;
246 }
247 return errno;
248 }
249 }
250 mutex->curr_locked = 1;
251 return APR_SUCCESS;
252 }
253 #endif
254
proc_mutex_posix_release(apr_proc_mutex_t * mutex)255 static apr_status_t proc_mutex_posix_release(apr_proc_mutex_t *mutex)
256 {
257 mutex->curr_locked = 0;
258 if (sem_post(mutex->os.psem_interproc) < 0) {
259 /* any failure is probably fatal, so no big deal to leave
260 * ->curr_locked at 0. */
261 return errno;
262 }
263 return APR_SUCCESS;
264 }
265
266 static const apr_proc_mutex_unix_lock_methods_t mutex_posixsem_methods =
267 {
268 #if APR_PROCESS_LOCK_IS_GLOBAL || !APR_HAS_THREADS || defined(POSIXSEM_IS_GLOBAL)
269 APR_PROCESS_LOCK_MECH_IS_GLOBAL,
270 #else
271 0,
272 #endif
273 proc_mutex_posix_create,
274 proc_mutex_posix_acquire,
275 proc_mutex_posix_tryacquire,
276 #if defined(HAVE_SEM_TIMEDWAIT)
277 proc_mutex_posix_timedacquire,
278 #else
279 proc_mutex_spinsleep_timedacquire,
280 #endif
281 proc_mutex_posix_release,
282 proc_mutex_posix_cleanup,
283 proc_mutex_no_child_init,
284 proc_mutex_no_perms_set,
285 APR_LOCK_POSIXSEM,
286 "posixsem"
287 };
288
289 #endif /* Posix sem implementation */
290
291 #if APR_HAS_SYSVSEM_SERIALIZE
292
293 static struct sembuf proc_mutex_op_on;
294 static struct sembuf proc_mutex_op_try;
295 static struct sembuf proc_mutex_op_off;
296
proc_mutex_sysv_setup(void)297 static void proc_mutex_sysv_setup(void)
298 {
299 proc_mutex_op_on.sem_num = 0;
300 proc_mutex_op_on.sem_op = -1;
301 proc_mutex_op_on.sem_flg = SEM_UNDO;
302 proc_mutex_op_try.sem_num = 0;
303 proc_mutex_op_try.sem_op = -1;
304 proc_mutex_op_try.sem_flg = SEM_UNDO | IPC_NOWAIT;
305 proc_mutex_op_off.sem_num = 0;
306 proc_mutex_op_off.sem_op = 1;
307 proc_mutex_op_off.sem_flg = SEM_UNDO;
308 }
309
proc_mutex_sysv_cleanup(void * mutex_)310 static apr_status_t proc_mutex_sysv_cleanup(void *mutex_)
311 {
312 apr_proc_mutex_t *mutex=mutex_;
313 union semun ick;
314
315 if (mutex->os.crossproc != -1) {
316 ick.val = 0;
317 semctl(mutex->os.crossproc, 0, IPC_RMID, ick);
318 }
319 return APR_SUCCESS;
320 }
321
proc_mutex_sysv_create(apr_proc_mutex_t * new_mutex,const char * fname)322 static apr_status_t proc_mutex_sysv_create(apr_proc_mutex_t *new_mutex,
323 const char *fname)
324 {
325 union semun ick;
326 apr_status_t rv;
327
328 new_mutex->os.crossproc = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
329 if (new_mutex->os.crossproc == -1) {
330 rv = errno;
331 proc_mutex_sysv_cleanup(new_mutex);
332 return rv;
333 }
334 ick.val = 1;
335 if (semctl(new_mutex->os.crossproc, 0, SETVAL, ick) < 0) {
336 rv = errno;
337 proc_mutex_sysv_cleanup(new_mutex);
338 new_mutex->os.crossproc = -1;
339 return rv;
340 }
341 new_mutex->curr_locked = 0;
342 apr_pool_cleanup_register(new_mutex->pool,
343 (void *)new_mutex, apr_proc_mutex_cleanup,
344 apr_pool_cleanup_null);
345 return APR_SUCCESS;
346 }
347
proc_mutex_sysv_acquire(apr_proc_mutex_t * mutex)348 static apr_status_t proc_mutex_sysv_acquire(apr_proc_mutex_t *mutex)
349 {
350 int rc;
351
352 do {
353 rc = semop(mutex->os.crossproc, &proc_mutex_op_on, 1);
354 } while (rc < 0 && errno == EINTR);
355 if (rc < 0) {
356 return errno;
357 }
358 mutex->curr_locked = 1;
359 return APR_SUCCESS;
360 }
361
proc_mutex_sysv_tryacquire(apr_proc_mutex_t * mutex)362 static apr_status_t proc_mutex_sysv_tryacquire(apr_proc_mutex_t *mutex)
363 {
364 int rc;
365
366 do {
367 rc = semop(mutex->os.crossproc, &proc_mutex_op_try, 1);
368 } while (rc < 0 && errno == EINTR);
369 if (rc < 0) {
370 if (errno == EAGAIN) {
371 return APR_EBUSY;
372 }
373 return errno;
374 }
375 mutex->curr_locked = 1;
376 return APR_SUCCESS;
377 }
378
379 #if defined(HAVE_SEMTIMEDOP)
proc_mutex_sysv_timedacquire(apr_proc_mutex_t * mutex,apr_interval_time_t timeout)380 static apr_status_t proc_mutex_sysv_timedacquire(apr_proc_mutex_t *mutex,
381 apr_interval_time_t timeout)
382 {
383 if (timeout <= 0) {
384 apr_status_t rv = proc_mutex_sysv_tryacquire(mutex);
385 return (rv == APR_EBUSY) ? APR_TIMEUP : rv;
386 }
387 else {
388 int rc;
389 struct timespec reltime;
390
391 reltime.tv_sec = apr_time_sec(timeout);
392 reltime.tv_nsec = apr_time_usec(timeout) * 1000; /* nanoseconds */
393
394 do {
395 rc = semtimedop(mutex->os.crossproc, &proc_mutex_op_on, 1,
396 &reltime);
397 } while (rc < 0 && errno == EINTR);
398 if (rc < 0) {
399 if (errno == EAGAIN) {
400 return APR_TIMEUP;
401 }
402 return errno;
403 }
404 }
405 mutex->curr_locked = 1;
406 return APR_SUCCESS;
407 }
408 #endif
409
proc_mutex_sysv_release(apr_proc_mutex_t * mutex)410 static apr_status_t proc_mutex_sysv_release(apr_proc_mutex_t *mutex)
411 {
412 int rc;
413
414 mutex->curr_locked = 0;
415 do {
416 rc = semop(mutex->os.crossproc, &proc_mutex_op_off, 1);
417 } while (rc < 0 && errno == EINTR);
418 if (rc < 0) {
419 return errno;
420 }
421 return APR_SUCCESS;
422 }
423
proc_mutex_sysv_perms_set(apr_proc_mutex_t * mutex,apr_fileperms_t perms,apr_uid_t uid,apr_gid_t gid)424 static apr_status_t proc_mutex_sysv_perms_set(apr_proc_mutex_t *mutex,
425 apr_fileperms_t perms,
426 apr_uid_t uid,
427 apr_gid_t gid)
428 {
429
430 union semun ick;
431 struct semid_ds buf;
432 buf.sem_perm.uid = uid;
433 buf.sem_perm.gid = gid;
434 buf.sem_perm.mode = apr_unix_perms2mode(perms);
435 ick.buf = &buf;
436 if (semctl(mutex->os.crossproc, 0, IPC_SET, ick) < 0) {
437 return errno;
438 }
439 return APR_SUCCESS;
440 }
441
442 static const apr_proc_mutex_unix_lock_methods_t mutex_sysv_methods =
443 {
444 #if APR_PROCESS_LOCK_IS_GLOBAL || !APR_HAS_THREADS || defined(SYSVSEM_IS_GLOBAL)
445 APR_PROCESS_LOCK_MECH_IS_GLOBAL,
446 #else
447 0,
448 #endif
449 proc_mutex_sysv_create,
450 proc_mutex_sysv_acquire,
451 proc_mutex_sysv_tryacquire,
452 #if defined(HAVE_SEMTIMEDOP)
453 proc_mutex_sysv_timedacquire,
454 #else
455 proc_mutex_spinsleep_timedacquire,
456 #endif
457 proc_mutex_sysv_release,
458 proc_mutex_sysv_cleanup,
459 proc_mutex_no_child_init,
460 proc_mutex_sysv_perms_set,
461 APR_LOCK_SYSVSEM,
462 "sysvsem"
463 };
464
465 #endif /* SysV sem implementation */
466
467 #if APR_HAS_PROC_PTHREAD_SERIALIZE
468
469 #ifndef APR_USE_PROC_PTHREAD_MUTEX_COND
470 #define APR_USE_PROC_PTHREAD_MUTEX_COND \
471 (defined(HAVE_PTHREAD_CONDATTR_SETPSHARED) \
472 && !defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK))
473 #endif
474
475 /* The mmap()ed pthread_interproc is the native pthread_mutex_t followed
476 * by a refcounter to track children using it. We want to avoid calling
477 * pthread_mutex_destroy() on the shared mutex area while it is in use by
478 * another process, because this may mark the shared pthread_mutex_t as
479 * invalid for everyone, including forked children (unlike "sysvsem" for
480 * example), causing unexpected errors or deadlocks (PR 49504). So the
481 * last process (parent or child) referencing the mutex will effectively
482 * destroy it.
483 */
484 typedef struct {
485 #define proc_pthread_cast(m) \
486 ((proc_pthread_mutex_t *)(m)->os.pthread_interproc)
487 pthread_mutex_t mutex;
488 #define proc_pthread_mutex(m) \
489 (proc_pthread_cast(m)->mutex)
490 #if APR_USE_PROC_PTHREAD_MUTEX_COND
491 pthread_cond_t cond;
492 #define proc_pthread_mutex_cond(m) \
493 (proc_pthread_cast(m)->cond)
494 apr_int32_t cond_locked;
495 #define proc_pthread_mutex_cond_locked(m) \
496 (proc_pthread_cast(m)->cond_locked)
497 apr_uint32_t cond_num_waiters;
498 #define proc_pthread_mutex_cond_num_waiters(m) \
499 (proc_pthread_cast(m)->cond_num_waiters)
500 #define proc_pthread_mutex_is_cond(m) \
501 ((m)->pthread_refcounting && proc_pthread_mutex_cond_locked(m) != -1)
502 #endif /* APR_USE_PROC_PTHREAD_MUTEX_COND */
503 apr_uint32_t refcount;
504 #define proc_pthread_mutex_refcount(m) \
505 (proc_pthread_cast(m)->refcount)
506 } proc_pthread_mutex_t;
507
508
proc_pthread_mutex_inc(apr_proc_mutex_t * mutex)509 static APR_INLINE int proc_pthread_mutex_inc(apr_proc_mutex_t *mutex)
510 {
511 if (mutex->pthread_refcounting) {
512 apr_atomic_inc32(&proc_pthread_mutex_refcount(mutex));
513 return 1;
514 }
515 return 0;
516 }
517
proc_pthread_mutex_dec(apr_proc_mutex_t * mutex)518 static APR_INLINE int proc_pthread_mutex_dec(apr_proc_mutex_t *mutex)
519 {
520 if (mutex->pthread_refcounting) {
521 return apr_atomic_dec32(&proc_pthread_mutex_refcount(mutex));
522 }
523 return 0;
524 }
525
proc_pthread_mutex_unref(void * mutex_)526 static apr_status_t proc_pthread_mutex_unref(void *mutex_)
527 {
528 apr_proc_mutex_t *mutex=mutex_;
529 apr_status_t rv;
530
531 #if APR_USE_PROC_PTHREAD_MUTEX_COND
532 if (proc_pthread_mutex_is_cond(mutex)) {
533 mutex->curr_locked = 0;
534 }
535 else
536 #endif /* APR_USE_PROC_PTHREAD_MUTEX_COND */
537 if (mutex->curr_locked == 1) {
538 if ((rv = pthread_mutex_unlock(&proc_pthread_mutex(mutex)))) {
539 #ifdef HAVE_ZOS_PTHREADS
540 rv = errno;
541 #endif
542 return rv;
543 }
544 }
545 if (!proc_pthread_mutex_dec(mutex)) {
546 #if APR_USE_PROC_PTHREAD_MUTEX_COND
547 if (proc_pthread_mutex_is_cond(mutex) &&
548 (rv = pthread_cond_destroy(&proc_pthread_mutex_cond(mutex)))) {
549 #ifdef HAVE_ZOS_PTHREADS
550 rv = errno;
551 #endif
552 return rv;
553 }
554 #endif /* APR_USE_PROC_PTHREAD_MUTEX_COND */
555
556 if ((rv = pthread_mutex_destroy(&proc_pthread_mutex(mutex)))) {
557 #ifdef HAVE_ZOS_PTHREADS
558 rv = errno;
559 #endif
560 return rv;
561 }
562 }
563 return APR_SUCCESS;
564 }
565
proc_mutex_pthread_cleanup(void * mutex_)566 static apr_status_t proc_mutex_pthread_cleanup(void *mutex_)
567 {
568 apr_proc_mutex_t *mutex=mutex_;
569 apr_status_t rv;
570
571 /* curr_locked is set to -1 until the mutex has been created */
572 if (mutex->curr_locked != -1) {
573 if ((rv = proc_pthread_mutex_unref(mutex))) {
574 return rv;
575 }
576 }
577 if (munmap(mutex->os.pthread_interproc, sizeof(proc_pthread_mutex_t))) {
578 return errno;
579 }
580 return APR_SUCCESS;
581 }
582
proc_mutex_pthread_create(apr_proc_mutex_t * new_mutex,const char * fname)583 static apr_status_t proc_mutex_pthread_create(apr_proc_mutex_t *new_mutex,
584 const char *fname)
585 {
586 apr_status_t rv;
587 int fd;
588 pthread_mutexattr_t mattr;
589
590 fd = open("/dev/zero", O_RDWR);
591 if (fd < 0) {
592 return errno;
593 }
594
595 new_mutex->os.pthread_interproc = mmap(NULL, sizeof(proc_pthread_mutex_t),
596 PROT_READ | PROT_WRITE, MAP_SHARED,
597 fd, 0);
598 if (new_mutex->os.pthread_interproc == MAP_FAILED) {
599 new_mutex->os.pthread_interproc = NULL;
600 rv = errno;
601 close(fd);
602 return rv;
603 }
604 close(fd);
605
606 new_mutex->pthread_refcounting = 1;
607 new_mutex->curr_locked = -1; /* until the mutex has been created */
608 #if APR_USE_PROC_PTHREAD_MUTEX_COND
609 proc_pthread_mutex_cond_locked(new_mutex) = -1;
610 #endif
611
612 if ((rv = pthread_mutexattr_init(&mattr))) {
613 #ifdef HAVE_ZOS_PTHREADS
614 rv = errno;
615 #endif
616 proc_mutex_pthread_cleanup(new_mutex);
617 return rv;
618 }
619 if ((rv = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED))) {
620 #ifdef HAVE_ZOS_PTHREADS
621 rv = errno;
622 #endif
623 proc_mutex_pthread_cleanup(new_mutex);
624 pthread_mutexattr_destroy(&mattr);
625 return rv;
626 }
627
628 #if defined(HAVE_PTHREAD_MUTEX_ROBUST) || defined(HAVE_PTHREAD_MUTEX_ROBUST_NP)
629 #ifdef HAVE_PTHREAD_MUTEX_ROBUST
630 rv = pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST);
631 #else
632 rv = pthread_mutexattr_setrobust_np(&mattr, PTHREAD_MUTEX_ROBUST_NP);
633 #endif
634 if (rv) {
635 #ifdef HAVE_ZOS_PTHREADS
636 rv = errno;
637 #endif
638 proc_mutex_pthread_cleanup(new_mutex);
639 pthread_mutexattr_destroy(&mattr);
640 return rv;
641 }
642 if ((rv = pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT))) {
643 #ifdef HAVE_ZOS_PTHREADS
644 rv = errno;
645 #endif
646 proc_mutex_pthread_cleanup(new_mutex);
647 pthread_mutexattr_destroy(&mattr);
648 return rv;
649 }
650 #endif /* HAVE_PTHREAD_MUTEX_ROBUST[_NP] */
651
652 if ((rv = pthread_mutex_init(&proc_pthread_mutex(new_mutex), &mattr))) {
653 #ifdef HAVE_ZOS_PTHREADS
654 rv = errno;
655 #endif
656 proc_mutex_pthread_cleanup(new_mutex);
657 pthread_mutexattr_destroy(&mattr);
658 return rv;
659 }
660
661 proc_pthread_mutex_refcount(new_mutex) = 1; /* first/parent reference */
662 new_mutex->curr_locked = 0; /* mutex created now */
663
664 if ((rv = pthread_mutexattr_destroy(&mattr))) {
665 #ifdef HAVE_ZOS_PTHREADS
666 rv = errno;
667 #endif
668 proc_mutex_pthread_cleanup(new_mutex);
669 return rv;
670 }
671
672 apr_pool_cleanup_register(new_mutex->pool,
673 (void *)new_mutex,
674 apr_proc_mutex_cleanup,
675 apr_pool_cleanup_null);
676 return APR_SUCCESS;
677 }
678
proc_mutex_pthread_child_init(apr_proc_mutex_t ** mutex,apr_pool_t * pool,const char * fname)679 static apr_status_t proc_mutex_pthread_child_init(apr_proc_mutex_t **mutex,
680 apr_pool_t *pool,
681 const char *fname)
682 {
683 (*mutex)->curr_locked = 0;
684 if (proc_pthread_mutex_inc(*mutex)) {
685 apr_pool_cleanup_register(pool, *mutex, proc_pthread_mutex_unref,
686 apr_pool_cleanup_null);
687 }
688 return APR_SUCCESS;
689 }
690
proc_mutex_pthread_acquire_ex(apr_proc_mutex_t * mutex,apr_interval_time_t timeout)691 static apr_status_t proc_mutex_pthread_acquire_ex(apr_proc_mutex_t *mutex,
692 apr_interval_time_t timeout)
693 {
694 apr_status_t rv;
695
696 #if APR_USE_PROC_PTHREAD_MUTEX_COND
697 if (proc_pthread_mutex_is_cond(mutex)) {
698 if ((rv = pthread_mutex_lock(&proc_pthread_mutex(mutex)))) {
699 #ifdef HAVE_ZOS_PTHREADS
700 rv = errno;
701 #endif
702 #if defined(HAVE_PTHREAD_MUTEX_ROBUST) || defined(HAVE_PTHREAD_MUTEX_ROBUST_NP)
703 /* Okay, our owner died. Let's try to make it consistent again. */
704 if (rv == EOWNERDEAD) {
705 proc_pthread_mutex_dec(mutex);
706 #ifdef HAVE_PTHREAD_MUTEX_ROBUST
707 pthread_mutex_consistent(&proc_pthread_mutex(mutex));
708 #else
709 pthread_mutex_consistent_np(&proc_pthread_mutex(mutex));
710 #endif
711 }
712 else
713 #endif
714 return rv;
715 }
716
717 if (!proc_pthread_mutex_cond_locked(mutex)) {
718 rv = APR_SUCCESS;
719 }
720 else if (!timeout) {
721 rv = APR_TIMEUP;
722 }
723 else {
724 struct timespec abstime;
725
726 if (timeout > 0) {
727 timeout += apr_time_now();
728 abstime.tv_sec = apr_time_sec(timeout);
729 abstime.tv_nsec = apr_time_usec(timeout) * 1000; /* nanoseconds */
730 }
731
732 proc_pthread_mutex_cond_num_waiters(mutex)++;
733 do {
734 if (timeout < 0) {
735 rv = pthread_cond_wait(&proc_pthread_mutex_cond(mutex),
736 &proc_pthread_mutex(mutex));
737 if (rv) {
738 #ifdef HAVE_ZOS_PTHREADS
739 rv = errno;
740 #endif
741 break;
742 }
743 }
744 else {
745 rv = pthread_cond_timedwait(&proc_pthread_mutex_cond(mutex),
746 &proc_pthread_mutex(mutex),
747 &abstime);
748 if (rv) {
749 #ifdef HAVE_ZOS_PTHREADS
750 rv = errno;
751 #endif
752 if (rv == ETIMEDOUT) {
753 rv = APR_TIMEUP;
754 }
755 break;
756 }
757 }
758 } while (proc_pthread_mutex_cond_locked(mutex));
759 proc_pthread_mutex_cond_num_waiters(mutex)--;
760 }
761 if (rv != APR_SUCCESS) {
762 pthread_mutex_unlock(&proc_pthread_mutex(mutex));
763 return rv;
764 }
765
766 proc_pthread_mutex_cond_locked(mutex) = 1;
767
768 rv = pthread_mutex_unlock(&proc_pthread_mutex(mutex));
769 if (rv) {
770 #ifdef HAVE_ZOS_PTHREADS
771 rv = errno;
772 #endif
773 return rv;
774 }
775 }
776 else
777 #endif /* APR_USE_PROC_PTHREAD_MUTEX_COND */
778 {
779 if (timeout < 0) {
780 rv = pthread_mutex_lock(&proc_pthread_mutex(mutex));
781 if (rv) {
782 #ifdef HAVE_ZOS_PTHREADS
783 rv = errno;
784 #endif
785 }
786 }
787 else if (!timeout) {
788 rv = pthread_mutex_trylock(&proc_pthread_mutex(mutex));
789 if (rv) {
790 #ifdef HAVE_ZOS_PTHREADS
791 rv = errno;
792 #endif
793 if (rv == EBUSY) {
794 return APR_TIMEUP;
795 }
796 }
797 }
798 else
799 #if defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK)
800 {
801 struct timespec abstime;
802
803 timeout += apr_time_now();
804 abstime.tv_sec = apr_time_sec(timeout);
805 abstime.tv_nsec = apr_time_usec(timeout) * 1000; /* nanoseconds */
806
807 rv = pthread_mutex_timedlock(&proc_pthread_mutex(mutex), &abstime);
808 if (rv) {
809 #ifdef HAVE_ZOS_PTHREADS
810 rv = errno;
811 #endif
812 if (rv == ETIMEDOUT) {
813 return APR_TIMEUP;
814 }
815 }
816 }
817 if (rv) {
818 #if defined(HAVE_PTHREAD_MUTEX_ROBUST) || defined(HAVE_PTHREAD_MUTEX_ROBUST_NP)
819 /* Okay, our owner died. Let's try to make it consistent again. */
820 if (rv == EOWNERDEAD) {
821 proc_pthread_mutex_dec(mutex);
822 #ifdef HAVE_PTHREAD_MUTEX_ROBUST
823 pthread_mutex_consistent(&proc_pthread_mutex(mutex));
824 #else
825 pthread_mutex_consistent_np(&proc_pthread_mutex(mutex));
826 #endif
827 }
828 else
829 #endif
830 return rv;
831 }
832 #else /* !HAVE_PTHREAD_MUTEX_TIMEDLOCK */
833 return proc_mutex_spinsleep_timedacquire(mutex, timeout);
834 #endif
835 }
836
837 mutex->curr_locked = 1;
838 return APR_SUCCESS;
839 }
840
proc_mutex_pthread_acquire(apr_proc_mutex_t * mutex)841 static apr_status_t proc_mutex_pthread_acquire(apr_proc_mutex_t *mutex)
842 {
843 return proc_mutex_pthread_acquire_ex(mutex, -1);
844 }
845
proc_mutex_pthread_tryacquire(apr_proc_mutex_t * mutex)846 static apr_status_t proc_mutex_pthread_tryacquire(apr_proc_mutex_t *mutex)
847 {
848 apr_status_t rv = proc_mutex_pthread_acquire_ex(mutex, 0);
849 return (rv == APR_TIMEUP) ? APR_EBUSY : rv;
850 }
851
proc_mutex_pthread_timedacquire(apr_proc_mutex_t * mutex,apr_interval_time_t timeout)852 static apr_status_t proc_mutex_pthread_timedacquire(apr_proc_mutex_t *mutex,
853 apr_interval_time_t timeout)
854 {
855 return proc_mutex_pthread_acquire_ex(mutex, (timeout <= 0) ? 0 : timeout);
856 }
857
proc_mutex_pthread_release(apr_proc_mutex_t * mutex)858 static apr_status_t proc_mutex_pthread_release(apr_proc_mutex_t *mutex)
859 {
860 apr_status_t rv;
861
862 #if APR_USE_PROC_PTHREAD_MUTEX_COND
863 if (proc_pthread_mutex_is_cond(mutex)) {
864 if ((rv = pthread_mutex_lock(&proc_pthread_mutex(mutex)))) {
865 #ifdef HAVE_ZOS_PTHREADS
866 rv = errno;
867 #endif
868 #if defined(HAVE_PTHREAD_MUTEX_ROBUST) || defined(HAVE_PTHREAD_MUTEX_ROBUST_NP)
869 /* Okay, our owner died. Let's try to make it consistent again. */
870 if (rv == EOWNERDEAD) {
871 proc_pthread_mutex_dec(mutex);
872 #ifdef HAVE_PTHREAD_MUTEX_ROBUST
873 pthread_mutex_consistent(&proc_pthread_mutex(mutex));
874 #else
875 pthread_mutex_consistent_np(&proc_pthread_mutex(mutex));
876 #endif
877 }
878 else
879 #endif
880 return rv;
881 }
882
883 if (!proc_pthread_mutex_cond_locked(mutex)) {
884 rv = APR_EINVAL;
885 }
886 else if (!proc_pthread_mutex_cond_num_waiters(mutex)) {
887 rv = APR_SUCCESS;
888 }
889 else {
890 rv = pthread_cond_signal(&proc_pthread_mutex_cond(mutex));
891 #ifdef HAVE_ZOS_PTHREADS
892 if (rv) {
893 rv = errno;
894 }
895 #endif
896 }
897 if (rv != APR_SUCCESS) {
898 pthread_mutex_unlock(&proc_pthread_mutex(mutex));
899 return rv;
900 }
901
902 proc_pthread_mutex_cond_locked(mutex) = 0;
903 }
904 #endif /* APR_USE_PROC_PTHREAD_MUTEX_COND */
905
906 mutex->curr_locked = 0;
907 if ((rv = pthread_mutex_unlock(&proc_pthread_mutex(mutex)))) {
908 #ifdef HAVE_ZOS_PTHREADS
909 rv = errno;
910 #endif
911 return rv;
912 }
913
914 return APR_SUCCESS;
915 }
916
917 static const apr_proc_mutex_unix_lock_methods_t mutex_proc_pthread_methods =
918 {
919 APR_PROCESS_LOCK_MECH_IS_GLOBAL,
920 proc_mutex_pthread_create,
921 proc_mutex_pthread_acquire,
922 proc_mutex_pthread_tryacquire,
923 proc_mutex_pthread_timedacquire,
924 proc_mutex_pthread_release,
925 proc_mutex_pthread_cleanup,
926 proc_mutex_pthread_child_init,
927 proc_mutex_no_perms_set,
928 APR_LOCK_PROC_PTHREAD,
929 "pthread"
930 };
931
932 #if APR_USE_PROC_PTHREAD_MUTEX_COND
proc_mutex_pthread_cond_create(apr_proc_mutex_t * new_mutex,const char * fname)933 static apr_status_t proc_mutex_pthread_cond_create(apr_proc_mutex_t *new_mutex,
934 const char *fname)
935 {
936 apr_status_t rv;
937 pthread_condattr_t cattr;
938
939 rv = proc_mutex_pthread_create(new_mutex, fname);
940 if (rv != APR_SUCCESS) {
941 return rv;
942 }
943
944 if ((rv = pthread_condattr_init(&cattr))) {
945 #ifdef HAVE_ZOS_PTHREADS
946 rv = errno;
947 #endif
948 apr_pool_cleanup_run(new_mutex->pool, new_mutex,
949 apr_proc_mutex_cleanup);
950 return rv;
951 }
952 if ((rv = pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED))) {
953 #ifdef HAVE_ZOS_PTHREADS
954 rv = errno;
955 #endif
956 pthread_condattr_destroy(&cattr);
957 apr_pool_cleanup_run(new_mutex->pool, new_mutex,
958 apr_proc_mutex_cleanup);
959 return rv;
960 }
961 if ((rv = pthread_cond_init(&proc_pthread_mutex_cond(new_mutex),
962 &cattr))) {
963 #ifdef HAVE_ZOS_PTHREADS
964 rv = errno;
965 #endif
966 pthread_condattr_destroy(&cattr);
967 apr_pool_cleanup_run(new_mutex->pool, new_mutex,
968 apr_proc_mutex_cleanup);
969 return rv;
970 }
971 pthread_condattr_destroy(&cattr);
972
973 proc_pthread_mutex_cond_locked(new_mutex) = 0;
974 proc_pthread_mutex_cond_num_waiters(new_mutex) = 0;
975
976 return APR_SUCCESS;
977 }
978
979 static const apr_proc_mutex_unix_lock_methods_t mutex_proc_pthread_cond_methods =
980 {
981 APR_PROCESS_LOCK_MECH_IS_GLOBAL,
982 proc_mutex_pthread_cond_create,
983 proc_mutex_pthread_acquire,
984 proc_mutex_pthread_tryacquire,
985 proc_mutex_pthread_timedacquire,
986 proc_mutex_pthread_release,
987 proc_mutex_pthread_cleanup,
988 proc_mutex_pthread_child_init,
989 proc_mutex_no_perms_set,
990 APR_LOCK_PROC_PTHREAD,
991 "pthread"
992 };
993 #endif
994
995 #endif
996
997 #if APR_HAS_FCNTL_SERIALIZE
998
999 static struct flock proc_mutex_lock_it;
1000 static struct flock proc_mutex_unlock_it;
1001
1002 static apr_status_t proc_mutex_fcntl_release(apr_proc_mutex_t *);
1003
proc_mutex_fcntl_setup(void)1004 static void proc_mutex_fcntl_setup(void)
1005 {
1006 proc_mutex_lock_it.l_whence = SEEK_SET; /* from current point */
1007 proc_mutex_lock_it.l_start = 0; /* -"- */
1008 proc_mutex_lock_it.l_len = 0; /* until end of file */
1009 proc_mutex_lock_it.l_type = F_WRLCK; /* set exclusive/write lock */
1010 proc_mutex_lock_it.l_pid = 0; /* pid not actually interesting */
1011 proc_mutex_unlock_it.l_whence = SEEK_SET; /* from current point */
1012 proc_mutex_unlock_it.l_start = 0; /* -"- */
1013 proc_mutex_unlock_it.l_len = 0; /* until end of file */
1014 proc_mutex_unlock_it.l_type = F_UNLCK; /* set exclusive/write lock */
1015 proc_mutex_unlock_it.l_pid = 0; /* pid not actually interesting */
1016 }
1017
proc_mutex_fcntl_cleanup(void * mutex_)1018 static apr_status_t proc_mutex_fcntl_cleanup(void *mutex_)
1019 {
1020 apr_status_t status = APR_SUCCESS;
1021 apr_proc_mutex_t *mutex=mutex_;
1022
1023 if (mutex->curr_locked == 1) {
1024 status = proc_mutex_fcntl_release(mutex);
1025 if (status != APR_SUCCESS)
1026 return status;
1027 }
1028
1029 if (mutex->interproc) {
1030 status = apr_file_close(mutex->interproc);
1031 }
1032 if (!mutex->interproc_closing
1033 && mutex->os.crossproc != -1
1034 && close(mutex->os.crossproc) == -1
1035 && status == APR_SUCCESS) {
1036 status = errno;
1037 }
1038 return status;
1039 }
1040
proc_mutex_fcntl_create(apr_proc_mutex_t * new_mutex,const char * fname)1041 static apr_status_t proc_mutex_fcntl_create(apr_proc_mutex_t *new_mutex,
1042 const char *fname)
1043 {
1044 int rv;
1045
1046 if (fname) {
1047 new_mutex->fname = apr_pstrdup(new_mutex->pool, fname);
1048 rv = apr_file_open(&new_mutex->interproc, new_mutex->fname,
1049 APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_EXCL,
1050 APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD,
1051 new_mutex->pool);
1052 }
1053 else {
1054 new_mutex->fname = apr_pstrdup(new_mutex->pool, "/tmp/aprXXXXXX");
1055 rv = apr_file_mktemp(&new_mutex->interproc, new_mutex->fname,
1056 APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_EXCL,
1057 new_mutex->pool);
1058 }
1059
1060 if (rv != APR_SUCCESS) {
1061 return rv;
1062 }
1063
1064 new_mutex->os.crossproc = new_mutex->interproc->filedes;
1065 new_mutex->interproc_closing = 1;
1066 new_mutex->curr_locked = 0;
1067 unlink(new_mutex->fname);
1068 apr_pool_cleanup_register(new_mutex->pool,
1069 (void*)new_mutex,
1070 apr_proc_mutex_cleanup,
1071 apr_pool_cleanup_null);
1072 return APR_SUCCESS;
1073 }
1074
proc_mutex_fcntl_acquire(apr_proc_mutex_t * mutex)1075 static apr_status_t proc_mutex_fcntl_acquire(apr_proc_mutex_t *mutex)
1076 {
1077 int rc;
1078
1079 do {
1080 rc = fcntl(mutex->os.crossproc, F_SETLKW, &proc_mutex_lock_it);
1081 } while (rc < 0 && errno == EINTR);
1082 if (rc < 0) {
1083 return errno;
1084 }
1085 mutex->curr_locked=1;
1086 return APR_SUCCESS;
1087 }
1088
proc_mutex_fcntl_tryacquire(apr_proc_mutex_t * mutex)1089 static apr_status_t proc_mutex_fcntl_tryacquire(apr_proc_mutex_t *mutex)
1090 {
1091 int rc;
1092
1093 do {
1094 rc = fcntl(mutex->os.crossproc, F_SETLK, &proc_mutex_lock_it);
1095 } while (rc < 0 && errno == EINTR);
1096 if (rc < 0) {
1097 #if FCNTL_TRYACQUIRE_EACCES
1098 if (errno == EACCES) {
1099 #else
1100 if (errno == EAGAIN) {
1101 #endif
1102 return APR_EBUSY;
1103 }
1104 return errno;
1105 }
1106 mutex->curr_locked = 1;
1107 return APR_SUCCESS;
1108 }
1109
1110 static apr_status_t proc_mutex_fcntl_release(apr_proc_mutex_t *mutex)
1111 {
1112 int rc;
1113
1114 mutex->curr_locked=0;
1115 do {
1116 rc = fcntl(mutex->os.crossproc, F_SETLKW, &proc_mutex_unlock_it);
1117 } while (rc < 0 && errno == EINTR);
1118 if (rc < 0) {
1119 return errno;
1120 }
1121 return APR_SUCCESS;
1122 }
1123
1124 static apr_status_t proc_mutex_fcntl_perms_set(apr_proc_mutex_t *mutex,
1125 apr_fileperms_t perms,
1126 apr_uid_t uid,
1127 apr_gid_t gid)
1128 {
1129
1130 if (mutex->fname) {
1131 if (!(perms & APR_FPROT_GSETID))
1132 gid = -1;
1133 if (fchown(mutex->os.crossproc, uid, gid) < 0) {
1134 return errno;
1135 }
1136 }
1137 return APR_SUCCESS;
1138 }
1139
1140 static const apr_proc_mutex_unix_lock_methods_t mutex_fcntl_methods =
1141 {
1142 #if APR_PROCESS_LOCK_IS_GLOBAL || !APR_HAS_THREADS || defined(FCNTL_IS_GLOBAL)
1143 APR_PROCESS_LOCK_MECH_IS_GLOBAL,
1144 #else
1145 0,
1146 #endif
1147 proc_mutex_fcntl_create,
1148 proc_mutex_fcntl_acquire,
1149 proc_mutex_fcntl_tryacquire,
1150 proc_mutex_spinsleep_timedacquire,
1151 proc_mutex_fcntl_release,
1152 proc_mutex_fcntl_cleanup,
1153 proc_mutex_no_child_init,
1154 proc_mutex_fcntl_perms_set,
1155 APR_LOCK_FCNTL,
1156 "fcntl"
1157 };
1158
1159 #endif /* fcntl implementation */
1160
1161 #if APR_HAS_FLOCK_SERIALIZE
1162
1163 static apr_status_t proc_mutex_flock_release(apr_proc_mutex_t *);
1164
1165 static apr_status_t proc_mutex_flock_cleanup(void *mutex_)
1166 {
1167 apr_status_t status = APR_SUCCESS;
1168 apr_proc_mutex_t *mutex=mutex_;
1169
1170 if (mutex->curr_locked == 1) {
1171 status = proc_mutex_flock_release(mutex);
1172 if (status != APR_SUCCESS)
1173 return status;
1174 }
1175 if (mutex->interproc) { /* if it was opened properly */
1176 status = apr_file_close(mutex->interproc);
1177 }
1178 if (!mutex->interproc_closing
1179 && mutex->os.crossproc != -1
1180 && close(mutex->os.crossproc) == -1
1181 && status == APR_SUCCESS) {
1182 status = errno;
1183 }
1184 if (mutex->fname) {
1185 unlink(mutex->fname);
1186 }
1187 return status;
1188 }
1189
1190 static apr_status_t proc_mutex_flock_create(apr_proc_mutex_t *new_mutex,
1191 const char *fname)
1192 {
1193 int rv;
1194
1195 if (fname) {
1196 new_mutex->fname = apr_pstrdup(new_mutex->pool, fname);
1197 rv = apr_file_open(&new_mutex->interproc, new_mutex->fname,
1198 APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_EXCL,
1199 APR_UREAD | APR_UWRITE,
1200 new_mutex->pool);
1201 }
1202 else {
1203 new_mutex->fname = apr_pstrdup(new_mutex->pool, "/tmp/aprXXXXXX");
1204 rv = apr_file_mktemp(&new_mutex->interproc, new_mutex->fname,
1205 APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_EXCL,
1206 new_mutex->pool);
1207 }
1208
1209 if (rv != APR_SUCCESS) {
1210 proc_mutex_flock_cleanup(new_mutex);
1211 return rv;
1212 }
1213
1214 new_mutex->os.crossproc = new_mutex->interproc->filedes;
1215 new_mutex->interproc_closing = 1;
1216 new_mutex->curr_locked = 0;
1217 apr_pool_cleanup_register(new_mutex->pool, (void *)new_mutex,
1218 apr_proc_mutex_cleanup,
1219 apr_pool_cleanup_null);
1220 return APR_SUCCESS;
1221 }
1222
1223 static apr_status_t proc_mutex_flock_acquire(apr_proc_mutex_t *mutex)
1224 {
1225 int rc;
1226
1227 do {
1228 rc = flock(mutex->os.crossproc, LOCK_EX);
1229 } while (rc < 0 && errno == EINTR);
1230 if (rc < 0) {
1231 return errno;
1232 }
1233 mutex->curr_locked = 1;
1234 return APR_SUCCESS;
1235 }
1236
1237 static apr_status_t proc_mutex_flock_tryacquire(apr_proc_mutex_t *mutex)
1238 {
1239 int rc;
1240
1241 do {
1242 rc = flock(mutex->os.crossproc, LOCK_EX | LOCK_NB);
1243 } while (rc < 0 && errno == EINTR);
1244 if (rc < 0) {
1245 if (errno == EWOULDBLOCK || errno == EAGAIN) {
1246 return APR_EBUSY;
1247 }
1248 return errno;
1249 }
1250 mutex->curr_locked = 1;
1251 return APR_SUCCESS;
1252 }
1253
1254 static apr_status_t proc_mutex_flock_release(apr_proc_mutex_t *mutex)
1255 {
1256 int rc;
1257
1258 mutex->curr_locked = 0;
1259 do {
1260 rc = flock(mutex->os.crossproc, LOCK_UN);
1261 } while (rc < 0 && errno == EINTR);
1262 if (rc < 0) {
1263 return errno;
1264 }
1265 return APR_SUCCESS;
1266 }
1267
1268 static apr_status_t proc_mutex_flock_child_init(apr_proc_mutex_t **mutex,
1269 apr_pool_t *pool,
1270 const char *fname)
1271 {
1272 apr_proc_mutex_t *new_mutex;
1273 int rv;
1274
1275 if (!fname) {
1276 fname = (*mutex)->fname;
1277 if (!fname) {
1278 return APR_SUCCESS;
1279 }
1280 }
1281
1282 new_mutex = (apr_proc_mutex_t *)apr_pmemdup(pool, *mutex,
1283 sizeof(apr_proc_mutex_t));
1284 new_mutex->pool = pool;
1285 new_mutex->fname = apr_pstrdup(pool, fname);
1286 rv = apr_file_open(&new_mutex->interproc, new_mutex->fname,
1287 APR_FOPEN_WRITE, 0, new_mutex->pool);
1288 if (rv != APR_SUCCESS) {
1289 return rv;
1290 }
1291 new_mutex->os.crossproc = new_mutex->interproc->filedes;
1292 new_mutex->interproc_closing = 1;
1293
1294 *mutex = new_mutex;
1295 return APR_SUCCESS;
1296 }
1297
1298 static apr_status_t proc_mutex_flock_perms_set(apr_proc_mutex_t *mutex,
1299 apr_fileperms_t perms,
1300 apr_uid_t uid,
1301 apr_gid_t gid)
1302 {
1303
1304 if (mutex->fname) {
1305 if (!(perms & APR_FPROT_GSETID))
1306 gid = -1;
1307 if (fchown(mutex->os.crossproc, uid, gid) < 0) {
1308 return errno;
1309 }
1310 }
1311 return APR_SUCCESS;
1312 }
1313
1314 static const apr_proc_mutex_unix_lock_methods_t mutex_flock_methods =
1315 {
1316 #if APR_PROCESS_LOCK_IS_GLOBAL || !APR_HAS_THREADS || defined(FLOCK_IS_GLOBAL)
1317 APR_PROCESS_LOCK_MECH_IS_GLOBAL,
1318 #else
1319 0,
1320 #endif
1321 proc_mutex_flock_create,
1322 proc_mutex_flock_acquire,
1323 proc_mutex_flock_tryacquire,
1324 proc_mutex_spinsleep_timedacquire,
1325 proc_mutex_flock_release,
1326 proc_mutex_flock_cleanup,
1327 proc_mutex_flock_child_init,
1328 proc_mutex_flock_perms_set,
1329 APR_LOCK_FLOCK,
1330 "flock"
1331 };
1332
1333 #endif /* flock implementation */
1334
1335 void apr_proc_mutex_unix_setup_lock(void)
1336 {
1337 /* setup only needed for sysvsem and fnctl */
1338 #if APR_HAS_SYSVSEM_SERIALIZE
1339 proc_mutex_sysv_setup();
1340 #endif
1341 #if APR_HAS_FCNTL_SERIALIZE
1342 proc_mutex_fcntl_setup();
1343 #endif
1344 }
1345
1346 static apr_status_t proc_mutex_choose_method(apr_proc_mutex_t *new_mutex,
1347 apr_lockmech_e mech,
1348 apr_os_proc_mutex_t *ospmutex)
1349 {
1350 #if APR_HAS_PROC_PTHREAD_SERIALIZE
1351 new_mutex->os.pthread_interproc = NULL;
1352 #endif
1353 #if APR_HAS_POSIXSEM_SERIALIZE
1354 new_mutex->os.psem_interproc = NULL;
1355 #endif
1356 #if APR_HAS_SYSVSEM_SERIALIZE || APR_HAS_FCNTL_SERIALIZE || APR_HAS_FLOCK_SERIALIZE
1357 new_mutex->os.crossproc = -1;
1358
1359 #if APR_HAS_FCNTL_SERIALIZE || APR_HAS_FLOCK_SERIALIZE
1360 new_mutex->interproc = NULL;
1361 new_mutex->interproc_closing = 0;
1362 #endif
1363 #endif
1364
1365 switch (mech) {
1366 case APR_LOCK_FCNTL:
1367 #if APR_HAS_FCNTL_SERIALIZE
1368 new_mutex->meth = &mutex_fcntl_methods;
1369 if (ospmutex) {
1370 if (ospmutex->crossproc == -1) {
1371 return APR_EINVAL;
1372 }
1373 new_mutex->os.crossproc = ospmutex->crossproc;
1374 }
1375 #else
1376 return APR_ENOTIMPL;
1377 #endif
1378 break;
1379 case APR_LOCK_FLOCK:
1380 #if APR_HAS_FLOCK_SERIALIZE
1381 new_mutex->meth = &mutex_flock_methods;
1382 if (ospmutex) {
1383 if (ospmutex->crossproc == -1) {
1384 return APR_EINVAL;
1385 }
1386 new_mutex->os.crossproc = ospmutex->crossproc;
1387 }
1388 #else
1389 return APR_ENOTIMPL;
1390 #endif
1391 break;
1392 case APR_LOCK_SYSVSEM:
1393 #if APR_HAS_SYSVSEM_SERIALIZE
1394 new_mutex->meth = &mutex_sysv_methods;
1395 if (ospmutex) {
1396 if (ospmutex->crossproc == -1) {
1397 return APR_EINVAL;
1398 }
1399 new_mutex->os.crossproc = ospmutex->crossproc;
1400 }
1401 #else
1402 return APR_ENOTIMPL;
1403 #endif
1404 break;
1405 case APR_LOCK_POSIXSEM:
1406 #if APR_HAS_POSIXSEM_SERIALIZE
1407 new_mutex->meth = &mutex_posixsem_methods;
1408 if (ospmutex) {
1409 if (ospmutex->psem_interproc == NULL) {
1410 return APR_EINVAL;
1411 }
1412 new_mutex->os.psem_interproc = ospmutex->psem_interproc;
1413 }
1414 #else
1415 return APR_ENOTIMPL;
1416 #endif
1417 break;
1418 case APR_LOCK_PROC_PTHREAD:
1419 #if APR_HAS_PROC_PTHREAD_SERIALIZE
1420 new_mutex->meth = &mutex_proc_pthread_methods;
1421 if (ospmutex) {
1422 if (ospmutex->pthread_interproc == NULL) {
1423 return APR_EINVAL;
1424 }
1425 new_mutex->os.pthread_interproc = ospmutex->pthread_interproc;
1426 }
1427 #else
1428 return APR_ENOTIMPL;
1429 #endif
1430 break;
1431 case APR_LOCK_DEFAULT_TIMED:
1432 #if APR_HAS_PROC_PTHREAD_SERIALIZE \
1433 && (APR_USE_PROC_PTHREAD_MUTEX_COND \
1434 || defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK)) \
1435 && defined(HAVE_PTHREAD_MUTEX_ROBUST)
1436 #if APR_USE_PROC_PTHREAD_MUTEX_COND
1437 new_mutex->meth = &mutex_proc_pthread_cond_methods;
1438 #else
1439 new_mutex->meth = &mutex_proc_pthread_methods;
1440 #endif
1441 if (ospmutex) {
1442 if (ospmutex->pthread_interproc == NULL) {
1443 return APR_EINVAL;
1444 }
1445 new_mutex->os.pthread_interproc = ospmutex->pthread_interproc;
1446 }
1447 break;
1448 #elif APR_HAS_SYSVSEM_SERIALIZE && defined(HAVE_SEMTIMEDOP)
1449 new_mutex->meth = &mutex_sysv_methods;
1450 if (ospmutex) {
1451 if (ospmutex->crossproc == -1) {
1452 return APR_EINVAL;
1453 }
1454 new_mutex->os.crossproc = ospmutex->crossproc;
1455 }
1456 break;
1457 #elif APR_HAS_POSIXSEM_SERIALIZE && defined(HAVE_SEM_TIMEDWAIT)
1458 new_mutex->meth = &mutex_posixsem_methods;
1459 if (ospmutex) {
1460 if (ospmutex->psem_interproc == NULL) {
1461 return APR_EINVAL;
1462 }
1463 new_mutex->os.psem_interproc = ospmutex->psem_interproc;
1464 }
1465 break;
1466 #endif
1467 /* fall trough */
1468 case APR_LOCK_DEFAULT:
1469 #if APR_USE_FLOCK_SERIALIZE
1470 new_mutex->meth = &mutex_flock_methods;
1471 if (ospmutex) {
1472 if (ospmutex->crossproc == -1) {
1473 return APR_EINVAL;
1474 }
1475 new_mutex->os.crossproc = ospmutex->crossproc;
1476 }
1477 #elif APR_USE_SYSVSEM_SERIALIZE
1478 new_mutex->meth = &mutex_sysv_methods;
1479 if (ospmutex) {
1480 if (ospmutex->crossproc == -1) {
1481 return APR_EINVAL;
1482 }
1483 new_mutex->os.crossproc = ospmutex->crossproc;
1484 }
1485 #elif APR_USE_FCNTL_SERIALIZE
1486 new_mutex->meth = &mutex_fcntl_methods;
1487 if (ospmutex) {
1488 if (ospmutex->crossproc == -1) {
1489 return APR_EINVAL;
1490 }
1491 new_mutex->os.crossproc = ospmutex->crossproc;
1492 }
1493 #elif APR_USE_PROC_PTHREAD_SERIALIZE
1494 new_mutex->meth = &mutex_proc_pthread_methods;
1495 if (ospmutex) {
1496 if (ospmutex->pthread_interproc == NULL) {
1497 return APR_EINVAL;
1498 }
1499 new_mutex->os.pthread_interproc = ospmutex->pthread_interproc;
1500 }
1501 #elif APR_USE_POSIXSEM_SERIALIZE
1502 new_mutex->meth = &mutex_posixsem_methods;
1503 if (ospmutex) {
1504 if (ospmutex->psem_interproc == NULL) {
1505 return APR_EINVAL;
1506 }
1507 new_mutex->os.psem_interproc = ospmutex->psem_interproc;
1508 }
1509 #else
1510 return APR_ENOTIMPL;
1511 #endif
1512 break;
1513 default:
1514 return APR_ENOTIMPL;
1515 }
1516 return APR_SUCCESS;
1517 }
1518
1519 APR_DECLARE(const char *) apr_proc_mutex_defname(void)
1520 {
1521 apr_status_t rv;
1522 apr_proc_mutex_t mutex;
1523
1524 if ((rv = proc_mutex_choose_method(&mutex, APR_LOCK_DEFAULT,
1525 NULL)) != APR_SUCCESS) {
1526 return "unknown";
1527 }
1528
1529 return apr_proc_mutex_name(&mutex);
1530 }
1531
1532 static apr_status_t proc_mutex_create(apr_proc_mutex_t *new_mutex, apr_lockmech_e mech, const char *fname)
1533 {
1534 apr_status_t rv;
1535
1536 if ((rv = proc_mutex_choose_method(new_mutex, mech,
1537 NULL)) != APR_SUCCESS) {
1538 return rv;
1539 }
1540
1541 if ((rv = new_mutex->meth->create(new_mutex, fname)) != APR_SUCCESS) {
1542 return rv;
1543 }
1544
1545 return APR_SUCCESS;
1546 }
1547
1548 APR_DECLARE(apr_status_t) apr_proc_mutex_create(apr_proc_mutex_t **mutex,
1549 const char *fname,
1550 apr_lockmech_e mech,
1551 apr_pool_t *pool)
1552 {
1553 apr_proc_mutex_t *new_mutex;
1554 apr_status_t rv;
1555
1556 new_mutex = apr_pcalloc(pool, sizeof(apr_proc_mutex_t));
1557 new_mutex->pool = pool;
1558
1559 if ((rv = proc_mutex_create(new_mutex, mech, fname)) != APR_SUCCESS)
1560 return rv;
1561
1562 *mutex = new_mutex;
1563 return APR_SUCCESS;
1564 }
1565
1566 APR_DECLARE(apr_status_t) apr_proc_mutex_child_init(apr_proc_mutex_t **mutex,
1567 const char *fname,
1568 apr_pool_t *pool)
1569 {
1570 return (*mutex)->meth->child_init(mutex, pool, fname);
1571 }
1572
1573 APR_DECLARE(apr_status_t) apr_proc_mutex_lock(apr_proc_mutex_t *mutex)
1574 {
1575 return mutex->meth->acquire(mutex);
1576 }
1577
1578 APR_DECLARE(apr_status_t) apr_proc_mutex_trylock(apr_proc_mutex_t *mutex)
1579 {
1580 return mutex->meth->tryacquire(mutex);
1581 }
1582
1583 APR_DECLARE(apr_status_t) apr_proc_mutex_timedlock(apr_proc_mutex_t *mutex,
1584 apr_interval_time_t timeout)
1585 {
1586 #if APR_HAS_TIMEDLOCKS
1587 return mutex->meth->timedacquire(mutex, timeout);
1588 #else
1589 return APR_ENOTIMPL;
1590 #endif
1591 }
1592
1593 APR_DECLARE(apr_status_t) apr_proc_mutex_unlock(apr_proc_mutex_t *mutex)
1594 {
1595 return mutex->meth->release(mutex);
1596 }
1597
1598 APR_DECLARE(apr_status_t) apr_proc_mutex_cleanup(void *mutex)
1599 {
1600 return ((apr_proc_mutex_t *)mutex)->meth->cleanup(mutex);
1601 }
1602
1603 APR_DECLARE(apr_lockmech_e) apr_proc_mutex_mech(apr_proc_mutex_t *mutex)
1604 {
1605 return mutex->meth->mech;
1606 }
1607
1608 APR_DECLARE(const char *) apr_proc_mutex_name(apr_proc_mutex_t *mutex)
1609 {
1610 return mutex->meth->name;
1611 }
1612
1613 APR_DECLARE(const char *) apr_proc_mutex_lockfile(apr_proc_mutex_t *mutex)
1614 {
1615 /* POSIX sems use the fname field but don't use a file,
1616 * so be careful. */
1617 #if APR_HAS_FLOCK_SERIALIZE
1618 if (mutex->meth == &mutex_flock_methods) {
1619 return mutex->fname;
1620 }
1621 #endif
1622 #if APR_HAS_FCNTL_SERIALIZE
1623 if (mutex->meth == &mutex_fcntl_methods) {
1624 return mutex->fname;
1625 }
1626 #endif
1627 return NULL;
1628 }
1629
1630 APR_PERMS_SET_IMPLEMENT(proc_mutex)
1631 {
1632 apr_proc_mutex_t *mutex = (apr_proc_mutex_t *)theproc_mutex;
1633 return mutex->meth->perms_set(mutex, perms, uid, gid);
1634 }
1635
1636 APR_POOL_IMPLEMENT_ACCESSOR(proc_mutex)
1637
1638 /* Implement OS-specific accessors defined in apr_portable.h */
1639
1640 APR_DECLARE(apr_status_t) apr_os_proc_mutex_get_ex(apr_os_proc_mutex_t *ospmutex,
1641 apr_proc_mutex_t *pmutex,
1642 apr_lockmech_e *mech)
1643 {
1644 *ospmutex = pmutex->os;
1645 if (mech) {
1646 *mech = pmutex->meth->mech;
1647 }
1648 return APR_SUCCESS;
1649 }
1650
1651 APR_DECLARE(apr_status_t) apr_os_proc_mutex_get(apr_os_proc_mutex_t *ospmutex,
1652 apr_proc_mutex_t *pmutex)
1653 {
1654 return apr_os_proc_mutex_get_ex(ospmutex, pmutex, NULL);
1655 }
1656
1657 APR_DECLARE(apr_status_t) apr_os_proc_mutex_put_ex(apr_proc_mutex_t **pmutex,
1658 apr_os_proc_mutex_t *ospmutex,
1659 apr_lockmech_e mech,
1660 int register_cleanup,
1661 apr_pool_t *pool)
1662 {
1663 apr_status_t rv;
1664 if (pool == NULL) {
1665 return APR_ENOPOOL;
1666 }
1667
1668 if ((*pmutex) == NULL) {
1669 (*pmutex) = (apr_proc_mutex_t *)apr_pcalloc(pool,
1670 sizeof(apr_proc_mutex_t));
1671 (*pmutex)->pool = pool;
1672 }
1673 rv = proc_mutex_choose_method(*pmutex, mech, ospmutex);
1674 #if APR_HAS_FCNTL_SERIALIZE || APR_HAS_FLOCK_SERIALIZE
1675 if (rv == APR_SUCCESS) {
1676 rv = apr_os_file_put(&(*pmutex)->interproc, &(*pmutex)->os.crossproc,
1677 0, pool);
1678 }
1679 #endif
1680
1681 if (rv == APR_SUCCESS && register_cleanup) {
1682 apr_pool_cleanup_register(pool, *pmutex, apr_proc_mutex_cleanup,
1683 apr_pool_cleanup_null);
1684 }
1685 return rv;
1686 }
1687
1688 APR_DECLARE(apr_status_t) apr_os_proc_mutex_put(apr_proc_mutex_t **pmutex,
1689 apr_os_proc_mutex_t *ospmutex,
1690 apr_pool_t *pool)
1691 {
1692 return apr_os_proc_mutex_put_ex(pmutex, ospmutex, APR_LOCK_DEFAULT,
1693 0, pool);
1694 }
1695
1696