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 7                                                        |
   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         PHP_SYSVSEM_VERSION,
 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_resource *rsrc)
 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         zend_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(), "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, 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, 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, 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, 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, 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, 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, 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         RETVAL_RES(zend_register_resource(sem_ptr, php_sysvsem_module.le_sem));
 294         sem_ptr->id = Z_RES_HANDLE_P(return_value);
 295 }
 296 /* }}} */
 297 
 298 /* {{{ php_sysvsem_semop
 299  */
 300 static void php_sysvsem_semop(INTERNAL_FUNCTION_PARAMETERS, int acquire)
 301 {
 302         zval *arg_id;
 303         zend_bool nowait = 0;
 304         sysvsem_sem *sem_ptr;
 305         struct sembuf sop;
 306 
 307         if (acquire) {
 308                 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|b", &arg_id, &nowait) == FAILURE) {
 309                         return;
 310                 }
 311         } else {
 312                 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &arg_id) == FAILURE) {
 313                         return;
 314                 }
 315         }
 316 
 317         if ((sem_ptr = (sysvsem_sem *)zend_fetch_resource(Z_RES_P(arg_id), "SysV semaphore", php_sysvsem_module.le_sem)) == NULL) {
 318                 RETURN_FALSE;
 319         }
 320 
 321         if (!acquire && sem_ptr->count == 0) {
 322                 php_error_docref(NULL, E_WARNING, "SysV semaphore %ld (key 0x%x) is not currently acquired", Z_LVAL_P(arg_id), sem_ptr->key);
 323                 RETURN_FALSE;
 324         }
 325 
 326         sop.sem_num = SYSVSEM_SEM;
 327         sop.sem_op  = acquire ? -1 : 1;
 328         sop.sem_flg = SEM_UNDO | (nowait ? IPC_NOWAIT : 0);
 329 
 330         while (semop(sem_ptr->semid, &sop, 1) == -1) {
 331                 if (errno != EINTR) {
 332                         if (errno != EAGAIN) {
 333                                 php_error_docref(NULL, E_WARNING, "failed to %s key 0x%x: %s", acquire ? "acquire" : "release", sem_ptr->key, strerror(errno));
 334                         }
 335                         RETURN_FALSE;
 336                 }
 337         }
 338 
 339         sem_ptr->count -= acquire ? -1 : 1;
 340         RETURN_TRUE;
 341 }
 342 /* }}} */
 343 
 344 /* {{{ proto bool sem_acquire(resource id)
 345    Acquires the semaphore with the given id, blocking if necessary */
 346 PHP_FUNCTION(sem_acquire)
 347 {
 348         php_sysvsem_semop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
 349 }
 350 /* }}} */
 351 
 352 /* {{{ proto bool sem_release(resource id)
 353    Releases the semaphore with the given id */
 354 PHP_FUNCTION(sem_release)
 355 {
 356         php_sysvsem_semop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
 357 }
 358 /* }}} */
 359 
 360 /* {{{ proto bool sem_remove(resource id)
 361    Removes semaphore from Unix systems */
 362 
 363 /*
 364  * contributed by Gavin Sherry gavin@linuxworld.com.au
 365  * Fri Mar 16 00:50:13 EST 2001
 366  */
 367 
 368 PHP_FUNCTION(sem_remove)
 369 {
 370         zval *arg_id;
 371         sysvsem_sem *sem_ptr;
 372 #if HAVE_SEMUN
 373         union semun un;
 374         struct semid_ds buf;
 375 #endif
 376 
 377         if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &arg_id) == FAILURE) {
 378                 return;
 379         }
 380 
 381         if ((sem_ptr = (sysvsem_sem *)zend_fetch_resource(Z_RES_P(arg_id), "SysV semaphore", php_sysvsem_module.le_sem)) == NULL) {
 382                 RETURN_FALSE;
 383         }
 384 
 385 #if HAVE_SEMUN
 386         un.buf = &buf;
 387         if (semctl(sem_ptr->semid, 0, IPC_STAT, un) < 0) {
 388 #else
 389         if (semctl(sem_ptr->semid, 0, IPC_STAT, NULL) < 0) {
 390 #endif
 391                 php_error_docref(NULL, E_WARNING, "SysV semaphore %ld does not (any longer) exist", Z_LVAL_P(arg_id));
 392                 RETURN_FALSE;
 393         }
 394 
 395 #if HAVE_SEMUN
 396         if (semctl(sem_ptr->semid, 0, IPC_RMID, un) < 0) {
 397 #else
 398         if (semctl(sem_ptr->semid, 0, IPC_RMID, NULL) < 0) {
 399 #endif
 400                 php_error_docref(NULL, E_WARNING, "failed for SysV sempphore %ld: %s", Z_LVAL_P(arg_id), strerror(errno));
 401                 RETURN_FALSE;
 402         }
 403 
 404         /* let release_sysvsem_sem know we have removed
 405          * the semaphore to avoid issues with releasing.
 406          */
 407 
 408         sem_ptr->count = -1;
 409         RETURN_TRUE;
 410 }
 411 
 412 /* }}} */
 413 
 414 #endif /* HAVE_SYSVSEM */
 415 
 416 /*
 417  * Local variables:
 418  * tab-width: 4
 419  * c-basic-offset: 4
 420  * End:
 421  * vim600: sw=4 ts=4 fdm=marker
 422  * vim<600: sw=4 ts=4
 423  */

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