root/ext/sysvsem/sysvsem.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. release_sysvsem_sem
  2. PHP_MINIT_FUNCTION
  3. PHP_FUNCTION
  4. php_sysvsem_semop
  5. PHP_FUNCTION
  6. PHP_FUNCTION
  7. PHP_FUNCTION

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

/* [<][>][^][v][top][bottom][index][help] */