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