Annotation of embedaddon/php/ext/sysvsem/sysvsem.c, revision 1.1.1.2

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:  
1.1.1.2 ! misho      20: /* $Id$ */
1.1       misho      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:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>