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); 106 h2 = rshash(p); 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 ospmutex->crossproc = pmutex->interproc->filedes; 955#endif 956#if APR_HAS_PROC_PTHREAD_SERIALIZE 957 ospmutex->pthread_interproc = pmutex->pthread_interproc; 958#endif 959 return APR_SUCCESS; 960} 961 962APR_DECLARE(apr_status_t) apr_os_proc_mutex_put(apr_proc_mutex_t **pmutex, 963 apr_os_proc_mutex_t *ospmutex, 964 apr_pool_t *pool) 965{ 966 if (pool == NULL) { 967 return APR_ENOPOOL; 968 } 969 if ((*pmutex) == NULL) { 970 (*pmutex) = (apr_proc_mutex_t *)apr_pcalloc(pool, 971 sizeof(apr_proc_mutex_t)); 972 (*pmutex)->pool = pool; 973 } 974#if APR_HAS_SYSVSEM_SERIALIZE || APR_HAS_FCNTL_SERIALIZE || APR_HAS_FLOCK_SERIALIZE || APR_HAS_POSIXSEM_SERIALIZE 975 apr_os_file_put(&(*pmutex)->interproc, &ospmutex->crossproc, 0, pool); 976#endif 977#if APR_HAS_PROC_PTHREAD_SERIALIZE 978 (*pmutex)->pthread_interproc = ospmutex->pthread_interproc; 979#endif 980 return APR_SUCCESS; 981} 982 983