You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

390 lines
11 KiB

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