summaryrefslogtreecommitdiff
path: root/ext/sysvsem/sysvsem.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/sysvsem/sysvsem.c')
-rw-r--r--ext/sysvsem/sysvsem.c343
1 files changed, 343 insertions, 0 deletions
diff --git a/ext/sysvsem/sysvsem.c b/ext/sysvsem/sysvsem.c
new file mode 100644
index 0000000000..2c2a9ccbc0
--- /dev/null
+++ b/ext/sysvsem/sysvsem.c
@@ -0,0 +1,343 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP HTML Embedded Scripting Language Version 3.0 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997,1998 PHP Development Team (See Credits file) |
+ +----------------------------------------------------------------------+
+ | This program is free software; you can redistribute it and/or modify |
+ | it under the terms of one of the following licenses: |
+ | |
+ | A) the GNU General Public License as published by the Free Software |
+ | Foundation; either version 2 of the License, or (at your option) |
+ | any later version. |
+ | |
+ | B) the PHP License as published by the PHP Development Team and |
+ | included in the distribution in the file: LICENSE |
+ | |
+ | This program is distributed in the hope that it will be useful, |
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ | GNU General Public License for more details. |
+ | |
+ | You should have received a copy of both licenses referred to here. |
+ | If you did not, or have any questions about PHP licensing, please |
+ | contact core@php.net. |
+ +----------------------------------------------------------------------+
+ | Authors: Tom May <tom@go2net.com> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+/* This has been built and tested on Solaris 2.6 and Linux 2.1.122.
+ * It may not compile or execute correctly on other systems.
+ *
+ * sas: Works for me on Linux 2.0.36 and FreeBSD 3.0-current
+ */
+
+#ifndef MSVC5
+#include "config.h"
+#endif
+
+#if HAVE_SYSVSEM
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <errno.h>
+
+#include "php.h"
+#include "php3_sysvsem.h"
+
+
+function_entry sysvsem_functions[] = {
+ {"sem_get", php3_sysvsem_get, NULL},
+ {"sem_acquire", php3_sysvsem_acquire, NULL},
+ {"sem_release", php3_sysvsem_release, NULL},
+ {NULL, NULL, NULL}
+};
+
+php3_module_entry sysvsem_module_entry = {
+ "System V semaphores", sysvsem_functions, php3_minit_sysvsem, NULL, NULL, NULL, NULL, STANDARD_MODULE_PROPERTIES
+};
+
+#if COMPILE_DL
+php3_module_entry *get_module() { return &sysvsem_module_entry; }
+#endif
+
+
+THREAD_LS sysvsem_module php3_sysvsem_module;
+
+/* Semaphore functions using System V semaphores. Each semaphore
+ * actually consists of three semaphores allocated as a unit under the
+ * same key. Semaphore 0 (SYSVSEM_SEM) is the actual semaphore, it is
+ * initialized to max_acquire and decremented as processes acquire it.
+ * The value of semaphore 1 (SYSVSEM_USAGE) is a count of the number
+ * of processes using the semaphore. After calling semget(), if a
+ * process finds that the usage count is 1, it will set the value of
+ * SYSVSEM_SEM to max_acquire. This allows max_acquire to be set and
+ * track the PHP code without having a global init routine or external
+ * semaphore init code. Except see the bug regarding a race condition
+ * php3_sysvsem_get(). Semaphore 2 (SYSVSEM_SETVAL) serializes the
+ * calls to GETVAL SYSVSEM_USAGE and SETVAL SYSVSEM_SEM. It can be
+ * acquired only when it is zero.
+ */
+
+#define SYSVSEM_SEM 0
+#define SYSVSEM_USAGE 1
+#define SYSVSEM_SETVAL 2
+
+
+static void release_sysvsem_sem(sysvsem_sem *sem_ptr)
+{
+ struct sembuf sop[2];
+
+ /* Decrement the usage count. */
+
+ sop[0].sem_num = SYSVSEM_USAGE;
+ sop[0].sem_op = -1;
+ sop[0].sem_flg = SEM_UNDO;
+
+ /* Release the semaphore if it has been acquired but not released. */
+
+ if (sem_ptr->count) {
+ php3_error(E_WARNING, "Releasing SysV semaphore id %d key 0x%x in request cleanup", sem_ptr->id, sem_ptr->key);
+
+ sop[1].sem_num = SYSVSEM_SEM;
+ sop[1].sem_op = sem_ptr->count;
+ sop[1].sem_flg = SEM_UNDO;
+ }
+ if (semop(sem_ptr->semid, sop, sem_ptr->count ? 2 : 1) == -1) {
+ php3_error(E_WARNING, "semop() failed in release_sysvsem_sem for key 0x%x: %s", sem_ptr->key, strerror(errno));
+ }
+
+ efree(sem_ptr);
+}
+
+
+int php3_minit_sysvsem(INIT_FUNC_ARGS)
+{
+ php3_sysvsem_module.le_sem = register_list_destructors(release_sysvsem_sem, NULL);
+
+ return SUCCESS;
+}
+
+
+/* {{{ proto int sem_get(int key [, int max_acquire [, int perm]])
+ Return an id for the semaphore with the given key, and allow max_acquire (default 1) processes to acquire it simultaneously. */
+void php3_sysvsem_get(INTERNAL_FUNCTION_PARAMETERS)
+{
+ pval *arg_key, *arg_max_acquire, *arg_perm;
+ int key, max_acquire, perm;
+ int semid;
+ struct sembuf sop[3];
+ int count;
+ sysvsem_sem *sem_ptr;
+#if HAVE_SEMUN
+ union semun un;
+#endif
+
+ max_acquire = 1;
+ perm = 0666;
+
+ switch (ARG_COUNT(ht)) {
+ case 1:
+ if (getParameters(ht, 1, &arg_key)==FAILURE) {
+ RETURN_FALSE;
+ }
+ convert_to_long(arg_key);
+ key = (int)arg_key->value.lval;
+ break;
+ case 2:
+ if (getParameters(ht, 2, &arg_key, &arg_max_acquire)==FAILURE) {
+ RETURN_FALSE;
+ }
+ convert_to_long(arg_key);
+ key = (int)arg_key->value.lval;
+ convert_to_long(arg_max_acquire);
+ max_acquire = (int)arg_max_acquire->value.lval;
+ break;
+ case 3:
+ if (getParameters(ht, 3, &arg_key, &arg_max_acquire, &arg_perm)==FAILURE) {
+ RETURN_FALSE;
+ }
+ convert_to_long(arg_key);
+ key = (int)arg_key->value.lval;
+ convert_to_long(arg_max_acquire);
+ max_acquire = (int)arg_max_acquire->value.lval;
+ convert_to_long(arg_perm);
+ perm = (int)arg_perm->value.lval;
+ break;
+ default:
+ WRONG_PARAM_COUNT;
+ break;
+ }
+
+ /* Get/create the semaphore. Note that we rely on the semaphores
+ * being zeroed when they are created. Despite the fact that
+ * the(?) Linux semget() man page says they are not initialized,
+ * the kernel versions 2.0.x and 2.1.z do in fact zero them.
+ */
+
+ semid = semget(key, 3, perm|IPC_CREAT);
+ if (semid == -1) {
+ php3_error(E_WARNING, "semget() failed for key 0x%x: %s", key, strerror(errno));
+ RETURN_FALSE;
+ }
+
+ /* Find out how many processes are using this semaphore. Note
+ * that on Linux (at least) there is a race condition here because
+ * semaphore undo on process exit is not atomic, so we could
+ * acquire SYSVSEM_SETVAL before a crashed process has decremented
+ * SYSVSEM_USAGE in which case count will be greater than it
+ * should be and we won't set max_acquire. Fortunately this
+ * doesn't actually matter in practice.
+ */
+
+ /* Wait for sem 1 to be zero . . . */
+
+ sop[0].sem_num = SYSVSEM_SETVAL;
+ sop[0].sem_op = 0;
+ sop[0].sem_flg = 0;
+
+ /* . . . and increment it so it becomes non-zero . . . */
+
+ sop[1].sem_num = SYSVSEM_SETVAL;
+ sop[1].sem_op = 1;
+ sop[1].sem_flg = SEM_UNDO;
+
+ /* . . . and increment the usage count. */
+
+ sop[2].sem_num = SYSVSEM_USAGE;
+ sop[2].sem_op = 1;
+ sop[2].sem_flg = SEM_UNDO;
+ while (semop(semid, sop, 3) == -1) {
+ if (errno != EINTR) {
+ php3_error(E_WARNING, "semop() failed acquiring SYSVSEM_SETVAL for key 0x%x: %s", key, strerror(errno));
+ break;
+ }
+ }
+
+ /* Get the usage count. */
+#if HAVE_SEMUN
+ count = semctl(semid, SYSVSEM_USAGE, GETVAL, un);
+#else
+ count = semctl(semid, SYSVSEM_USAGE, GETVAL, NULL);
+#endif
+ if (count == -1) {
+ php3_error(E_WARNING, "semctl(GETVAL) failed for key 0x%x: %s", key, strerror(errno));
+ }
+
+ /* If we are the only user, then take this opportunity to set the max. */
+
+ if (count == 1) {
+#if HAVE_SEMUN
+ /* This is correct for Linux which has union semun. */
+ union semun semarg;
+ semarg.val = max_acquire;
+ if (semctl(semid, SYSVSEM_SEM, SETVAL, semarg) == -1) {
+ php3_error(E_WARNING, "semctl(SETVAL) failed for key 0x%x: %s", key, strerror(errno));
+ }
+#else
+ /* This is correct for Solaris 2.6 which does not have union semun. */
+ if (semctl(semid, SYSVSEM_SEM, SETVAL, &max_acquire) == -1) {
+ php3_error(E_WARNING, "semctl(SETVAL) failed for key 0x%x: %s", key, strerror(errno));
+ }
+#endif
+ }
+
+ /* Set semaphore 1 back to zero. */
+
+ sop[0].sem_num = SYSVSEM_SETVAL;
+ sop[0].sem_op = -1;
+ sop[0].sem_flg = SEM_UNDO;
+ while (semop(semid, sop, 1) == -1) {
+ if (errno != EINTR) {
+ php3_error(E_WARNING, "semop() failed releasing SYSVSEM_SETVAL for key 0x%x: %s", key, strerror(errno));
+ break;
+ }
+ }
+
+ sem_ptr = (sysvsem_sem *) emalloc(sizeof(sysvsem_sem));
+ sem_ptr->key = key;
+ sem_ptr->semid = semid;
+ sem_ptr->count = 0;
+
+ return_value->value.lval = php3_list_insert(sem_ptr, php3_sysvsem_module.le_sem);
+ return_value->type = IS_LONG;
+
+ sem_ptr->id = (int)return_value->value.lval;
+}
+/* }}} */
+
+static void _php3_sysvsem_semop(INTERNAL_FUNCTION_PARAMETERS, int acquire)
+{
+ pval *arg_id;
+ int id, type;
+ sysvsem_sem *sem_ptr;
+ struct sembuf sop;
+
+ switch(ARG_COUNT(ht)) {
+ case 1:
+ if (getParameters(ht, 1, &arg_id)==FAILURE) {
+ RETURN_FALSE;
+ }
+ convert_to_long(arg_id);
+ id = (int)arg_id->value.lval;
+ break;
+ default:
+ WRONG_PARAM_COUNT;
+ break;
+ }
+
+ sem_ptr = (sysvsem_sem *) php3_list_find(id, &type);
+ if (type!=php3_sysvsem_module.le_sem) {
+ php3_error(E_WARNING, "%d is not a SysV semaphore index", id);
+ RETURN_FALSE;
+ }
+
+ if (!acquire && sem_ptr->count == 0) {
+ php3_error(E_WARNING, "SysV semaphore index %d (key 0x%x) is not currently acquired", id, sem_ptr->key);
+ RETURN_FALSE;
+ }
+
+ sop.sem_num = SYSVSEM_SEM;
+ sop.sem_op = acquire ? -1 : 1;
+ sop.sem_flg = SEM_UNDO;
+
+ while (semop(sem_ptr->semid, &sop, 1) == -1) {
+ if (errno != EINTR) {
+ php3_error(E_WARNING, "semop(%s) failed for key 0x%x: %s",
+ acquire ? "acquire" : "release", sem_ptr->key, strerror(errno));
+ RETURN_FALSE;
+ }
+ }
+
+ sem_ptr->count -= acquire ? -1 : 1;
+ RETURN_TRUE;
+}
+
+
+/* {{{ proto int sem_acquire(int id)
+ Acquires the semaphore with the given id, blocking if necessary. */
+void php3_sysvsem_acquire(INTERNAL_FUNCTION_PARAMETERS)
+{
+ _php3_sysvsem_semop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
+}
+/* }}} */
+
+/* {{{ proto int sem_release(int id)
+ Releases the semaphore with the given id. */
+void php3_sysvsem_release(INTERNAL_FUNCTION_PARAMETERS)
+{
+ _php3_sysvsem_semop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+}
+/* }}} */
+
+#endif /* HAVE_SYSVSEM */
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */