xref: /php-src/ext/sysvshm/sysvshm.c (revision a01dd9fe)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Author: Christian Cartus <cartus@atrior.de>                          |
14    +----------------------------------------------------------------------+
15  */
16 
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20 
21 #include "php.h"
22 
23 #ifdef HAVE_SYSVSHM
24 
25 #include <errno.h>
26 
27 #include "php_sysvshm.h"
28 #include "sysvshm_arginfo.h"
29 #include "ext/standard/info.h"
30 #include "ext/standard/php_var.h"
31 #include "zend_smart_str.h"
32 #include "php_ini.h"
33 
34 /* SysvSharedMemory class */
35 
36 zend_class_entry *sysvshm_ce;
37 static zend_object_handlers sysvshm_object_handlers;
38 
sysvshm_from_obj(zend_object * obj)39 static inline sysvshm_shm *sysvshm_from_obj(zend_object *obj) {
40 	return (sysvshm_shm *)((char *)(obj) - XtOffsetOf(sysvshm_shm, std));
41 }
42 
43 #define Z_SYSVSHM_P(zv) sysvshm_from_obj(Z_OBJ_P(zv))
44 
sysvshm_create_object(zend_class_entry * class_type)45 static zend_object *sysvshm_create_object(zend_class_entry *class_type) {
46 	sysvshm_shm *intern = zend_object_alloc(sizeof(sysvshm_shm), class_type);
47 
48 	zend_object_std_init(&intern->std, class_type);
49 	object_properties_init(&intern->std, class_type);
50 
51 	return &intern->std;
52 }
53 
sysvshm_get_constructor(zend_object * object)54 static zend_function *sysvshm_get_constructor(zend_object *object) {
55 	zend_throw_error(NULL, "Cannot directly construct SysvSharedMemory, use shm_attach() instead");
56 	return NULL;
57 }
58 
sysvshm_free_obj(zend_object * object)59 static void sysvshm_free_obj(zend_object *object)
60 {
61 	sysvshm_shm *sysvshm = sysvshm_from_obj(object);
62 
63 	if (sysvshm->ptr) {
64 		shmdt((void *) sysvshm->ptr);
65 	}
66 
67 	zend_object_std_dtor(&sysvshm->std);
68 }
69 
70 /* {{{ sysvshm_module_entry */
71 zend_module_entry sysvshm_module_entry = {
72 	STANDARD_MODULE_HEADER,
73 	"sysvshm",
74 	ext_functions,
75 	PHP_MINIT(sysvshm),
76 	NULL,
77 	NULL,
78 	NULL,
79 	PHP_MINFO(sysvshm),
80 	PHP_SYSVSHM_VERSION,
81 	STANDARD_MODULE_PROPERTIES
82 };
83 /* }}} */
84 
85 #ifdef COMPILE_DL_SYSVSHM
86 ZEND_GET_MODULE(sysvshm)
87 #endif
88 
89 #undef shm_ptr					/* undefine AIX-specific macro */
90 
91 /* TODO: Make this thread-safe. */
92 sysvshm_module php_sysvshm;
93 
94 static int php_put_shm_data(sysvshm_chunk_head *ptr, zend_long key, const char *data, zend_long len);
95 static zend_long php_check_shm_data(sysvshm_chunk_head *ptr, zend_long key);
96 static int php_remove_shm_data(sysvshm_chunk_head *ptr, zend_long shm_varpos);
97 
98 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(sysvshm)99 PHP_MINIT_FUNCTION(sysvshm)
100 {
101 	sysvshm_ce = register_class_SysvSharedMemory();
102 	sysvshm_ce->create_object = sysvshm_create_object;
103 	sysvshm_ce->default_object_handlers = &sysvshm_object_handlers;
104 
105 	memcpy(&sysvshm_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
106 	sysvshm_object_handlers.offset = XtOffsetOf(sysvshm_shm, std);
107 	sysvshm_object_handlers.free_obj = sysvshm_free_obj;
108 	sysvshm_object_handlers.get_constructor = sysvshm_get_constructor;
109 	sysvshm_object_handlers.clone_obj = NULL;
110 	sysvshm_object_handlers.compare = zend_objects_not_comparable;
111 
112 	if (cfg_get_long("sysvshm.init_mem", &php_sysvshm.init_mem) == FAILURE) {
113 		php_sysvshm.init_mem=10000;
114 	}
115 	return SUCCESS;
116 }
117 /* }}} */
118 
119 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(sysvshm)120 PHP_MINFO_FUNCTION(sysvshm)
121 {
122 	php_info_print_table_start();
123 	php_info_print_table_row(2, "sysvshm support", "enabled");
124 	php_info_print_table_end();
125 }
126 /* }}} */
127 
128 /* {{{ Creates or open a shared memory segment */
PHP_FUNCTION(shm_attach)129 PHP_FUNCTION(shm_attach)
130 {
131 	sysvshm_shm *shm_list_ptr;
132 	char *shm_ptr;
133 	sysvshm_chunk_head *chunk_ptr;
134 	zend_long shm_key, shm_id, shm_size, shm_flag = 0666;
135 	bool shm_size_is_null = 1;
136 
137 	if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "l|l!l", &shm_key, &shm_size, &shm_size_is_null, &shm_flag)) {
138 		RETURN_THROWS();
139 	}
140 
141 	if (shm_size_is_null) {
142 		shm_size = php_sysvshm.init_mem;
143 	}
144 
145 	if (shm_size < 1) {
146 		zend_argument_value_error(2, "must be greater than 0");
147 		RETURN_THROWS();
148 	}
149 
150 	/* get the id from a specified key or create new shared memory */
151 	if ((shm_id = shmget(shm_key, 0, 0)) < 0) {
152 		if (shm_size < (zend_long)sizeof(sysvshm_chunk_head)) {
153 			php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": memorysize too small", shm_key);
154 			RETURN_FALSE;
155 		}
156 		if ((shm_id = shmget(shm_key, shm_size, shm_flag | IPC_CREAT | IPC_EXCL)) < 0) {
157 			php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", shm_key, strerror(errno));
158 			RETURN_FALSE;
159 		}
160 	}
161 
162 	if ((shm_ptr = shmat(shm_id, NULL, 0)) == (void *) -1) {
163 		php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", shm_key, strerror(errno));
164 		RETURN_FALSE;
165 	}
166 
167 	/* check if shm is already initialized */
168 	chunk_ptr = (sysvshm_chunk_head *) shm_ptr;
169 	if (strcmp((char*) &(chunk_ptr->magic), "PHP_SM") != 0) {
170 		strcpy((char*) &(chunk_ptr->magic), "PHP_SM");
171 		chunk_ptr->start = sizeof(sysvshm_chunk_head);
172 		chunk_ptr->end = chunk_ptr->start;
173 		chunk_ptr->total = shm_size;
174 		chunk_ptr->free = shm_size-chunk_ptr->end;
175 	}
176 
177 	object_init_ex(return_value, sysvshm_ce);
178 
179 	shm_list_ptr = Z_SYSVSHM_P(return_value);
180 
181 	shm_list_ptr->key = shm_key;
182 	shm_list_ptr->id = shm_id;
183 	shm_list_ptr->ptr = chunk_ptr;
184 }
185 /* }}} */
186 
187 /* {{{ Disconnects from shared memory segment */
PHP_FUNCTION(shm_detach)188 PHP_FUNCTION(shm_detach)
189 {
190 	zval *shm_id;
191 	sysvshm_shm *shm_list_ptr;
192 
193 	if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "O", &shm_id, sysvshm_ce)) {
194 		RETURN_THROWS();
195 	}
196 
197 	shm_list_ptr = Z_SYSVSHM_P(shm_id);
198 	if (!shm_list_ptr->ptr) {
199 		zend_throw_error(NULL, "Shared memory block has already been destroyed");
200 		RETURN_THROWS();
201 	}
202 
203 	shmdt((void *) shm_list_ptr->ptr);
204 	shm_list_ptr->ptr = NULL;
205 
206 	RETURN_TRUE;
207 }
208 /* }}} */
209 
210 /* {{{ Removes shared memory from Unix systems */
PHP_FUNCTION(shm_remove)211 PHP_FUNCTION(shm_remove)
212 {
213 	zval *shm_id;
214 	sysvshm_shm *shm_list_ptr;
215 
216 	if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "O", &shm_id, sysvshm_ce)) {
217 		RETURN_THROWS();
218 	}
219 
220 	shm_list_ptr = Z_SYSVSHM_P(shm_id);
221 	if (!shm_list_ptr->ptr) {
222 		zend_throw_error(NULL, "Shared memory block has already been destroyed");
223 		RETURN_THROWS();
224 	}
225 
226 	if (shmctl(shm_list_ptr->id, IPC_RMID, NULL) < 0) {
227 		php_error_docref(NULL, E_WARNING, "Failed for key 0x%x, id " ZEND_LONG_FMT ": %s", shm_list_ptr->key, Z_LVAL_P(shm_id), strerror(errno));
228 		RETURN_FALSE;
229 	}
230 
231 	RETURN_TRUE;
232 }
233 /* }}} */
234 
235 /* {{{ Inserts or updates a variable in shared memory */
PHP_FUNCTION(shm_put_var)236 PHP_FUNCTION(shm_put_var)
237 {
238 	zval *shm_id, *arg_var;
239 	int ret;
240 	zend_long shm_key;
241 	sysvshm_shm *shm_list_ptr;
242 	smart_str shm_var = {0};
243 	php_serialize_data_t var_hash;
244 
245 	if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "Olz", &shm_id, sysvshm_ce, &shm_key, &arg_var)) {
246 		RETURN_THROWS();
247 	}
248 
249 	shm_list_ptr = Z_SYSVSHM_P(shm_id);
250 	if (!shm_list_ptr->ptr) {
251 		zend_throw_error(NULL, "Shared memory block has already been destroyed");
252 		RETURN_THROWS();
253 	}
254 
255 	/* setup string-variable and serialize */
256 	PHP_VAR_SERIALIZE_INIT(var_hash);
257 	php_var_serialize(&shm_var, arg_var, &var_hash);
258 	PHP_VAR_SERIALIZE_DESTROY(var_hash);
259 
260 	/* insert serialized variable into shared memory */
261 	ret = php_put_shm_data(shm_list_ptr->ptr, shm_key, shm_var.s? ZSTR_VAL(shm_var.s) : NULL, shm_var.s? ZSTR_LEN(shm_var.s) : 0);
262 
263 	/* free string */
264 	smart_str_free(&shm_var);
265 
266 	if (ret == -1) {
267 		php_error_docref(NULL, E_WARNING, "Not enough shared memory left");
268 		RETURN_FALSE;
269 	}
270 	RETURN_TRUE;
271 }
272 /* }}} */
273 
274 /* {{{ Returns a variable from shared memory */
PHP_FUNCTION(shm_get_var)275 PHP_FUNCTION(shm_get_var)
276 {
277 	zval *shm_id;
278 	zend_long shm_key;
279 	sysvshm_shm *shm_list_ptr;
280 	char *shm_data;
281 	zend_long shm_varpos;
282 	sysvshm_chunk *shm_var;
283 	php_unserialize_data_t var_hash;
284 
285 	if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &shm_id, sysvshm_ce, &shm_key)) {
286 		RETURN_THROWS();
287 	}
288 
289 	shm_list_ptr = Z_SYSVSHM_P(shm_id);
290 	if (!shm_list_ptr->ptr) {
291 		zend_throw_error(NULL, "Shared memory block has already been destroyed");
292 		RETURN_THROWS();
293 	}
294 
295 	/* setup string-variable and serialize */
296 	/* get serialized variable from shared memory */
297 	shm_varpos = php_check_shm_data(shm_list_ptr->ptr, shm_key);
298 
299 	if (shm_varpos < 0) {
300 		php_error_docref(NULL, E_WARNING, "Variable key " ZEND_LONG_FMT " doesn't exist", shm_key);
301 		RETURN_FALSE;
302 	}
303 	shm_var = (sysvshm_chunk*) ((char *)shm_list_ptr->ptr + shm_varpos);
304 	shm_data = &shm_var->mem;
305 
306 	PHP_VAR_UNSERIALIZE_INIT(var_hash);
307 	if (php_var_unserialize(return_value, (const unsigned char **) &shm_data, (unsigned char *) shm_data + shm_var->length, &var_hash) != 1) {
308 		php_error_docref(NULL, E_WARNING, "Variable data in shared memory is corrupted");
309 		RETVAL_FALSE;
310 	}
311 	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
312 }
313 /* }}} */
314 
315 /* {{{ Checks whether a specific entry exists */
PHP_FUNCTION(shm_has_var)316 PHP_FUNCTION(shm_has_var)
317 {
318 	zval *shm_id;
319 	zend_long shm_key;
320 	sysvshm_shm *shm_list_ptr;
321 
322 	if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &shm_id, sysvshm_ce, &shm_key)) {
323 		RETURN_THROWS();
324 	}
325 
326 	shm_list_ptr = Z_SYSVSHM_P(shm_id);
327 	if (!shm_list_ptr->ptr) {
328 		zend_throw_error(NULL, "Shared memory block has already been destroyed");
329 		RETURN_THROWS();
330 	}
331 
332 	RETURN_BOOL(php_check_shm_data(shm_list_ptr->ptr, shm_key) >= 0);
333 }
334 /* }}} */
335 
336 /* {{{ Removes variable from shared memory */
PHP_FUNCTION(shm_remove_var)337 PHP_FUNCTION(shm_remove_var)
338 {
339 	zval *shm_id;
340 	zend_long shm_key, shm_varpos;
341 	sysvshm_shm *shm_list_ptr;
342 
343 	if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &shm_id, sysvshm_ce, &shm_key)) {
344 		RETURN_THROWS();
345 	}
346 
347 	shm_list_ptr = Z_SYSVSHM_P(shm_id);
348 	if (!shm_list_ptr->ptr) {
349 		zend_throw_error(NULL, "Shared memory block has already been destroyed");
350 		RETURN_THROWS();
351 	}
352 
353 	shm_varpos = php_check_shm_data(shm_list_ptr->ptr, shm_key);
354 
355 	if (shm_varpos < 0) {
356 		php_error_docref(NULL, E_WARNING, "Variable key " ZEND_LONG_FMT " doesn't exist", shm_key);
357 		RETURN_FALSE;
358 	}
359 	php_remove_shm_data((shm_list_ptr->ptr), shm_varpos);
360 	RETURN_TRUE;
361 }
362 /* }}} */
363 
364 /* {{{ php_put_shm_data
365  * inserts an ascii-string into shared memory */
php_put_shm_data(sysvshm_chunk_head * ptr,zend_long key,const char * data,zend_long len)366 static int php_put_shm_data(sysvshm_chunk_head *ptr, zend_long key, const char *data, zend_long len)
367 {
368 	sysvshm_chunk *shm_var;
369 	zend_long total_size;
370 	zend_long shm_varpos;
371 
372 	total_size = ((zend_long) (len + sizeof(sysvshm_chunk) - 1) / sizeof(zend_long)) * sizeof(zend_long) + sizeof(zend_long); /* zend_long alligment */
373 
374 	if ((shm_varpos = php_check_shm_data(ptr, key)) > 0) {
375 		php_remove_shm_data(ptr, shm_varpos);
376 	}
377 
378 	if (ptr->free < total_size) {
379 		return -1; /* not enough memory */
380 	}
381 
382 	shm_var = (sysvshm_chunk *) ((char *) ptr + ptr->end);
383 	shm_var->key = key;
384 	shm_var->length = len;
385 	shm_var->next = total_size;
386 	memcpy(&(shm_var->mem), data, len);
387 	ptr->end += total_size;
388 	ptr->free -= total_size;
389 	return 0;
390 }
391 /* }}} */
392 
393 /* {{{ php_check_shm_data */
php_check_shm_data(sysvshm_chunk_head * ptr,zend_long key)394 static zend_long php_check_shm_data(sysvshm_chunk_head *ptr, zend_long key)
395 {
396 	zend_long pos;
397 	sysvshm_chunk *shm_var;
398 
399 	ZEND_ASSERT(ptr);
400 
401 	pos = ptr->start;
402 
403 	for (;;) {
404 		if (pos >= ptr->end) {
405 			return -1;
406 		}
407 		shm_var = (sysvshm_chunk*) ((char *) ptr + pos);
408 		if (shm_var->key == key) {
409 			return pos;
410 		}
411 		pos += shm_var->next;
412 
413 		if (shm_var->next <= 0 || pos < ptr->start) {
414 			return -1;
415 		}
416 	}
417 	return -1;
418 }
419 /* }}} */
420 
421 /* {{{ php_remove_shm_data */
php_remove_shm_data(sysvshm_chunk_head * ptr,zend_long shm_varpos)422 static int php_remove_shm_data(sysvshm_chunk_head *ptr, zend_long shm_varpos)
423 {
424 	sysvshm_chunk *chunk_ptr, *next_chunk_ptr;
425 	zend_long memcpy_len;
426 
427 	ZEND_ASSERT(ptr);
428 
429 	chunk_ptr = (sysvshm_chunk *) ((char *) ptr + shm_varpos);
430 	next_chunk_ptr = (sysvshm_chunk *) ((char *) ptr + shm_varpos + chunk_ptr->next);
431 
432 	memcpy_len = ptr->end-shm_varpos - chunk_ptr->next;
433 	ptr->free += chunk_ptr->next;
434 	ptr->end -= chunk_ptr->next;
435 	if (memcpy_len > 0) {
436 		memmove(chunk_ptr, next_chunk_ptr, memcpy_len);
437 	}
438 	return 0;
439 }
440 /* }}} */
441 
442 #endif /* HAVE_SYSVSHM */
443