Return to sysvsem.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / sysvsem |
1.1 ! misho 1: /* ! 2: +----------------------------------------------------------------------+ ! 3: | PHP Version 5 | ! 4: +----------------------------------------------------------------------+ ! 5: | Copyright (c) 1997-2012 The PHP Group | ! 6: +----------------------------------------------------------------------+ ! 7: | This source file is subject to version 3.01 of the PHP license, | ! 8: | that is bundled with this package in the file LICENSE, and is | ! 9: | available through the world-wide-web at the following url: | ! 10: | http://www.php.net/license/3_01.txt | ! 11: | If you did not receive a copy of the PHP license and are unable to | ! 12: | obtain it through the world-wide-web, please send a note to | ! 13: | license@php.net so we can mail you a copy immediately. | ! 14: +----------------------------------------------------------------------+ ! 15: | Authors: Tom May <tom@go2net.com> | ! 16: | Gavin Sherry <gavin@linuxworld.com.au> | ! 17: +----------------------------------------------------------------------+ ! 18: */ ! 19: ! 20: /* $Id: sysvsem.c 321634 2012-01-01 13:15:04Z felipe $ */ ! 21: ! 22: /* Latest update build anc tested on Linux 2.2.14 ! 23: * ! 24: * This has been built and tested on Solaris 2.6 and Linux 2.1.122. ! 25: * It may not compile or execute correctly on other systems. ! 26: * ! 27: * sas: Works for me on Linux 2.0.36 and FreeBSD 3.0-current ! 28: */ ! 29: ! 30: #ifdef HAVE_CONFIG_H ! 31: #include "config.h" ! 32: #endif ! 33: ! 34: #include "php.h" ! 35: ! 36: #if HAVE_SYSVSEM ! 37: ! 38: #include <sys/types.h> ! 39: #include <sys/ipc.h> ! 40: #include <sys/sem.h> ! 41: #include <errno.h> ! 42: ! 43: #include "php_sysvsem.h" ! 44: ! 45: #if !HAVE_SEMUN ! 46: ! 47: union semun { ! 48: int val; /* value for SETVAL */ ! 49: struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ ! 50: unsigned short int *array; /* array for GETALL, SETALL */ ! 51: struct seminfo *__buf; /* buffer for IPC_INFO */ ! 52: }; ! 53: ! 54: #undef HAVE_SEMUN ! 55: #define HAVE_SEMUN 1 ! 56: ! 57: #endif ! 58: ! 59: /* {{{ arginfo */ ! 60: ZEND_BEGIN_ARG_INFO_EX(arginfo_sem_get, 0, 0, 1) ! 61: ZEND_ARG_INFO(0, key) ! 62: ZEND_ARG_INFO(0, max_acquire) ! 63: ZEND_ARG_INFO(0, perm) ! 64: ZEND_ARG_INFO(0, auto_release) ! 65: ZEND_END_ARG_INFO() ! 66: ! 67: ZEND_BEGIN_ARG_INFO_EX(arginfo_sem_acquire, 0, 0, 1) ! 68: ZEND_ARG_INFO(0, sem_identifier) ! 69: ZEND_END_ARG_INFO() ! 70: ! 71: ZEND_BEGIN_ARG_INFO_EX(arginfo_sem_release, 0, 0, 1) ! 72: ZEND_ARG_INFO(0, sem_identifier) ! 73: ZEND_END_ARG_INFO() ! 74: ! 75: ZEND_BEGIN_ARG_INFO_EX(arginfo_sem_remove, 0, 0, 1) ! 76: ZEND_ARG_INFO(0, sem_identifier) ! 77: ZEND_END_ARG_INFO() ! 78: /* }}} */ ! 79: ! 80: /* {{{ sysvsem_functions[] ! 81: */ ! 82: const zend_function_entry sysvsem_functions[] = { ! 83: PHP_FE(sem_get, arginfo_sem_get) ! 84: PHP_FE(sem_acquire, arginfo_sem_acquire) ! 85: PHP_FE(sem_release, arginfo_sem_release) ! 86: PHP_FE(sem_remove, arginfo_sem_remove) ! 87: PHP_FE_END ! 88: }; ! 89: /* }}} */ ! 90: ! 91: /* {{{ sysvsem_module_entry ! 92: */ ! 93: zend_module_entry sysvsem_module_entry = { ! 94: STANDARD_MODULE_HEADER, ! 95: "sysvsem", ! 96: sysvsem_functions, ! 97: PHP_MINIT(sysvsem), ! 98: NULL, ! 99: NULL, ! 100: NULL, ! 101: NULL, ! 102: NO_VERSION_YET, ! 103: STANDARD_MODULE_PROPERTIES ! 104: }; ! 105: /* }}} */ ! 106: ! 107: #ifdef COMPILE_DL_SYSVSEM ! 108: ZEND_GET_MODULE(sysvsem) ! 109: #endif ! 110: ! 111: ! 112: THREAD_LS sysvsem_module php_sysvsem_module; ! 113: ! 114: /* Semaphore functions using System V semaphores. Each semaphore ! 115: * actually consists of three semaphores allocated as a unit under the ! 116: * same key. Semaphore 0 (SYSVSEM_SEM) is the actual semaphore, it is ! 117: * initialized to max_acquire and decremented as processes acquire it. ! 118: * The value of semaphore 1 (SYSVSEM_USAGE) is a count of the number ! 119: * of processes using the semaphore. After calling semget(), if a ! 120: * process finds that the usage count is 1, it will set the value of ! 121: * SYSVSEM_SEM to max_acquire. This allows max_acquire to be set and ! 122: * track the PHP code without having a global init routine or external ! 123: * semaphore init code. Except see the bug regarding a race condition ! 124: * php_sysvsem_get(). Semaphore 2 (SYSVSEM_SETVAL) serializes the ! 125: * calls to GETVAL SYSVSEM_USAGE and SETVAL SYSVSEM_SEM. It can be ! 126: * acquired only when it is zero. ! 127: */ ! 128: ! 129: #define SYSVSEM_SEM 0 ! 130: #define SYSVSEM_USAGE 1 ! 131: #define SYSVSEM_SETVAL 2 ! 132: ! 133: /* {{{ release_sysvsem_sem ! 134: */ ! 135: static void release_sysvsem_sem(zend_rsrc_list_entry *rsrc TSRMLS_DC) ! 136: { ! 137: sysvsem_sem *sem_ptr = (sysvsem_sem *)rsrc->ptr; ! 138: struct sembuf sop[2]; ! 139: int opcount = 1; ! 140: /* ! 141: * if count == -1, semaphore has been removed ! 142: * Need better way to handle this ! 143: */ ! 144: ! 145: if (sem_ptr->count == -1 || !sem_ptr->auto_release) { ! 146: efree(sem_ptr); ! 147: return; ! 148: } ! 149: /* Decrement the usage count. */ ! 150: ! 151: sop[0].sem_num = SYSVSEM_USAGE; ! 152: sop[0].sem_op = -1; ! 153: sop[0].sem_flg = SEM_UNDO; ! 154: ! 155: /* Release the semaphore if it has been acquired but not released. */ ! 156: ! 157: if (sem_ptr->count) { ! 158: ! 159: sop[1].sem_num = SYSVSEM_SEM; ! 160: sop[1].sem_op = sem_ptr->count; ! 161: sop[1].sem_flg = SEM_UNDO; ! 162: ! 163: opcount++; ! 164: } ! 165: ! 166: semop(sem_ptr->semid, sop, opcount); ! 167: efree(sem_ptr); ! 168: } ! 169: /* }}} */ ! 170: ! 171: /* {{{ PHP_MINIT_FUNCTION ! 172: */ ! 173: PHP_MINIT_FUNCTION(sysvsem) ! 174: { ! 175: php_sysvsem_module.le_sem = zend_register_list_destructors_ex(release_sysvsem_sem, NULL, "sysvsem", module_number); ! 176: return SUCCESS; ! 177: } ! 178: /* }}} */ ! 179: ! 180: #define SETVAL_WANTS_PTR ! 181: ! 182: #if defined(_AIX) ! 183: #undef SETVAL_WANTS_PTR ! 184: #endif ! 185: ! 186: /* {{{ proto resource sem_get(int key [, int max_acquire [, int perm [, int auto_release]]) ! 187: Return an id for the semaphore with the given key, and allow max_acquire (default 1) processes to acquire it simultaneously */ ! 188: PHP_FUNCTION(sem_get) ! 189: { ! 190: long key, max_acquire = 1, perm = 0666, auto_release = 1; ! 191: int semid; ! 192: struct sembuf sop[3]; ! 193: int count; ! 194: sysvsem_sem *sem_ptr; ! 195: ! 196: if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|lll", &key, &max_acquire, &perm, &auto_release)) { ! 197: RETURN_FALSE; ! 198: } ! 199: ! 200: /* Get/create the semaphore. Note that we rely on the semaphores ! 201: * being zeroed when they are created. Despite the fact that ! 202: * the(?) Linux semget() man page says they are not initialized, ! 203: * the kernel versions 2.0.x and 2.1.z do in fact zero them. ! 204: */ ! 205: ! 206: semid = semget(key, 3, perm|IPC_CREAT); ! 207: if (semid == -1) { ! 208: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno)); ! 209: RETURN_FALSE; ! 210: } ! 211: ! 212: /* Find out how many processes are using this semaphore. Note ! 213: * that on Linux (at least) there is a race condition here because ! 214: * semaphore undo on process exit is not atomic, so we could ! 215: * acquire SYSVSEM_SETVAL before a crashed process has decremented ! 216: * SYSVSEM_USAGE in which case count will be greater than it ! 217: * should be and we won't set max_acquire. Fortunately this ! 218: * doesn't actually matter in practice. ! 219: */ ! 220: ! 221: /* Wait for sem 1 to be zero . . . */ ! 222: ! 223: sop[0].sem_num = SYSVSEM_SETVAL; ! 224: sop[0].sem_op = 0; ! 225: sop[0].sem_flg = 0; ! 226: ! 227: /* . . . and increment it so it becomes non-zero . . . */ ! 228: ! 229: sop[1].sem_num = SYSVSEM_SETVAL; ! 230: sop[1].sem_op = 1; ! 231: sop[1].sem_flg = SEM_UNDO; ! 232: ! 233: /* . . . and increment the usage count. */ ! 234: ! 235: sop[2].sem_num = SYSVSEM_USAGE; ! 236: sop[2].sem_op = 1; ! 237: sop[2].sem_flg = SEM_UNDO; ! 238: while (semop(semid, sop, 3) == -1) { ! 239: if (errno != EINTR) { ! 240: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed acquiring SYSVSEM_SETVAL for key 0x%lx: %s", key, strerror(errno)); ! 241: break; ! 242: } ! 243: } ! 244: ! 245: /* Get the usage count. */ ! 246: count = semctl(semid, SYSVSEM_USAGE, GETVAL, NULL); ! 247: if (count == -1) { ! 248: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno)); ! 249: } ! 250: ! 251: /* If we are the only user, then take this opportunity to set the max. */ ! 252: ! 253: if (count == 1) { ! 254: #if HAVE_SEMUN ! 255: /* This is correct for Linux which has union semun. */ ! 256: union semun semarg; ! 257: semarg.val = max_acquire; ! 258: if (semctl(semid, SYSVSEM_SEM, SETVAL, semarg) == -1) { ! 259: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno)); ! 260: } ! 261: #elif defined(SETVAL_WANTS_PTR) ! 262: /* This is correct for Solaris 2.6 which does not have union semun. */ ! 263: if (semctl(semid, SYSVSEM_SEM, SETVAL, &max_acquire) == -1) { ! 264: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno)); ! 265: } ! 266: #else ! 267: /* This works for i.e. AIX */ ! 268: if (semctl(semid, SYSVSEM_SEM, SETVAL, max_acquire) == -1) { ! 269: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno)); ! 270: } ! 271: #endif ! 272: } ! 273: ! 274: /* Set semaphore 1 back to zero. */ ! 275: ! 276: sop[0].sem_num = SYSVSEM_SETVAL; ! 277: sop[0].sem_op = -1; ! 278: sop[0].sem_flg = SEM_UNDO; ! 279: while (semop(semid, sop, 1) == -1) { ! 280: if (errno != EINTR) { ! 281: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed releasing SYSVSEM_SETVAL for key 0x%lx: %s", key, strerror(errno)); ! 282: break; ! 283: } ! 284: } ! 285: ! 286: sem_ptr = (sysvsem_sem *) emalloc(sizeof(sysvsem_sem)); ! 287: sem_ptr->key = key; ! 288: sem_ptr->semid = semid; ! 289: sem_ptr->count = 0; ! 290: sem_ptr->auto_release = auto_release; ! 291: ! 292: sem_ptr->id = ZEND_REGISTER_RESOURCE(return_value, sem_ptr, php_sysvsem_module.le_sem); ! 293: } ! 294: /* }}} */ ! 295: ! 296: /* {{{ php_sysvsem_semop ! 297: */ ! 298: static void php_sysvsem_semop(INTERNAL_FUNCTION_PARAMETERS, int acquire) ! 299: { ! 300: zval *arg_id; ! 301: sysvsem_sem *sem_ptr; ! 302: struct sembuf sop; ! 303: ! 304: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg_id) == FAILURE) { ! 305: return; ! 306: } ! 307: ! 308: ZEND_FETCH_RESOURCE(sem_ptr, sysvsem_sem *, &arg_id, -1, "SysV semaphore", php_sysvsem_module.le_sem); ! 309: ! 310: if (!acquire && sem_ptr->count == 0) { ! 311: php_error_docref(NULL TSRMLS_CC, E_WARNING, "SysV semaphore %ld (key 0x%x) is not currently acquired", Z_LVAL_P(arg_id), sem_ptr->key); ! 312: RETURN_FALSE; ! 313: } ! 314: ! 315: sop.sem_num = SYSVSEM_SEM; ! 316: sop.sem_op = acquire ? -1 : 1; ! 317: sop.sem_flg = SEM_UNDO; ! 318: ! 319: while (semop(sem_ptr->semid, &sop, 1) == -1) { ! 320: if (errno != EINTR) { ! 321: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to %s key 0x%x: %s", acquire ? "acquire" : "release", sem_ptr->key, strerror(errno)); ! 322: RETURN_FALSE; ! 323: } ! 324: } ! 325: ! 326: sem_ptr->count -= acquire ? -1 : 1; ! 327: RETURN_TRUE; ! 328: } ! 329: /* }}} */ ! 330: ! 331: /* {{{ proto bool sem_acquire(resource id) ! 332: Acquires the semaphore with the given id, blocking if necessary */ ! 333: PHP_FUNCTION(sem_acquire) ! 334: { ! 335: php_sysvsem_semop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); ! 336: } ! 337: /* }}} */ ! 338: ! 339: /* {{{ proto bool sem_release(resource id) ! 340: Releases the semaphore with the given id */ ! 341: PHP_FUNCTION(sem_release) ! 342: { ! 343: php_sysvsem_semop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); ! 344: } ! 345: /* }}} */ ! 346: ! 347: /* {{{ proto bool sem_remove(resource id) ! 348: Removes semaphore from Unix systems */ ! 349: ! 350: /* ! 351: * contributed by Gavin Sherry gavin@linuxworld.com.au ! 352: * Fri Mar 16 00:50:13 EST 2001 ! 353: */ ! 354: ! 355: PHP_FUNCTION(sem_remove) ! 356: { ! 357: zval *arg_id; ! 358: sysvsem_sem *sem_ptr; ! 359: #if HAVE_SEMUN ! 360: union semun un; ! 361: struct semid_ds buf; ! 362: #endif ! 363: ! 364: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg_id) == FAILURE) { ! 365: return; ! 366: } ! 367: ! 368: ZEND_FETCH_RESOURCE(sem_ptr, sysvsem_sem *, &arg_id, -1, "SysV semaphore", php_sysvsem_module.le_sem); ! 369: ! 370: #if HAVE_SEMUN ! 371: un.buf = &buf; ! 372: if (semctl(sem_ptr->semid, 0, IPC_STAT, un) < 0) { ! 373: #else ! 374: if (semctl(sem_ptr->semid, 0, IPC_STAT, NULL) < 0) { ! 375: #endif ! 376: php_error_docref(NULL TSRMLS_CC, E_WARNING, "SysV semaphore %ld does not (any longer) exist", Z_LVAL_P(arg_id)); ! 377: RETURN_FALSE; ! 378: } ! 379: ! 380: #if HAVE_SEMUN ! 381: if (semctl(sem_ptr->semid, 0, IPC_RMID, un) < 0) { ! 382: #else ! 383: if (semctl(sem_ptr->semid, 0, IPC_RMID, NULL) < 0) { ! 384: #endif ! 385: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for SysV sempphore %ld: %s", Z_LVAL_P(arg_id), strerror(errno)); ! 386: RETURN_FALSE; ! 387: } ! 388: ! 389: /* let release_sysvsem_sem know we have removed ! 390: * the semaphore to avoid issues with releasing. ! 391: */ ! 392: ! 393: sem_ptr->count = -1; ! 394: RETURN_TRUE; ! 395: } ! 396: ! 397: /* }}} */ ! 398: ! 399: #endif /* HAVE_SYSVSEM */ ! 400: ! 401: /* ! 402: * Local variables: ! 403: * tab-width: 4 ! 404: * c-basic-offset: 4 ! 405: * End: ! 406: * vim600: sw=4 ts=4 fdm=marker ! 407: * vim<600: sw=4 ts=4 ! 408: */