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