1 /*
2 +----------------------------------------------------------------------+
3 | PHP version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 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: Slava Poliakov <hackie@prohost.org> |
16 | Ilia Alshanetsky <ilia@prohost.org> |
17 +----------------------------------------------------------------------+
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "php.h"
25 #include "php_ini.h"
26 #include "Zend/zend_interfaces.h"
27 #include "php_shmop.h"
28 #include "shmop_arginfo.h"
29
30 # ifndef PHP_WIN32
31 # include <sys/ipc.h>
32 # include <sys/shm.h>
33 #else
34 #include "tsrm_win32.h"
35 #endif
36
37
38 #ifdef HAVE_SHMOP
39
40 #include "ext/standard/info.h"
41
42 /* {{{ shmop_module_entry */
43 zend_module_entry shmop_module_entry = {
44 STANDARD_MODULE_HEADER,
45 "shmop",
46 ext_functions,
47 PHP_MINIT(shmop),
48 NULL,
49 NULL,
50 NULL,
51 PHP_MINFO(shmop),
52 PHP_SHMOP_VERSION,
53 STANDARD_MODULE_PROPERTIES
54 };
55 /* }}} */
56
57 #ifdef COMPILE_DL_SHMOP
58 ZEND_GET_MODULE(shmop)
59 #endif
60
61 typedef struct php_shmop
62 {
63 int shmid;
64 key_t key;
65 int shmflg;
66 int shmatflg;
67 char *addr;
68 zend_long size;
69 zend_object std;
70 } php_shmop;
71
72 zend_class_entry *shmop_ce;
73 static zend_object_handlers shmop_object_handlers;
74
shmop_from_obj(zend_object * obj)75 static inline php_shmop *shmop_from_obj(zend_object *obj)
76 {
77 return (php_shmop *)((char *)(obj) - XtOffsetOf(php_shmop, std));
78 }
79
80 #define Z_SHMOP_P(zv) shmop_from_obj(Z_OBJ_P(zv))
81
shmop_create_object(zend_class_entry * class_type)82 static zend_object *shmop_create_object(zend_class_entry *class_type)
83 {
84 php_shmop *intern = zend_object_alloc(sizeof(php_shmop), class_type);
85
86 zend_object_std_init(&intern->std, class_type);
87 object_properties_init(&intern->std, class_type);
88 intern->std.handlers = &shmop_object_handlers;
89
90 return &intern->std;
91 }
92
shmop_get_constructor(zend_object * object)93 static zend_function *shmop_get_constructor(zend_object *object)
94 {
95 zend_throw_error(NULL, "Cannot directly construct Shmop, use shmop_open() instead");
96 return NULL;
97 }
98
shmop_free_obj(zend_object * object)99 static void shmop_free_obj(zend_object *object)
100 {
101 php_shmop *shmop = shmop_from_obj(object);
102
103 shmdt(shmop->addr);
104
105 zend_object_std_dtor(&shmop->std);
106 }
107
108 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(shmop)109 PHP_MINIT_FUNCTION(shmop)
110 {
111 zend_class_entry ce;
112 INIT_CLASS_ENTRY(ce, "Shmop", class_Shmop_methods);
113 shmop_ce = zend_register_internal_class(&ce);
114 shmop_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES;
115 shmop_ce->create_object = shmop_create_object;
116 shmop_ce->serialize = zend_class_serialize_deny;
117 shmop_ce->unserialize = zend_class_unserialize_deny;
118
119 memcpy(&shmop_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
120 shmop_object_handlers.offset = XtOffsetOf(php_shmop, std);
121 shmop_object_handlers.free_obj = shmop_free_obj;
122 shmop_object_handlers.get_constructor = shmop_get_constructor;
123 shmop_object_handlers.clone_obj = NULL;
124 shmop_object_handlers.compare = zend_objects_not_comparable;
125
126 return SUCCESS;
127 }
128 /* }}} */
129
130 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(shmop)131 PHP_MINFO_FUNCTION(shmop)
132 {
133 php_info_print_table_start();
134 php_info_print_table_row(2, "shmop support", "enabled");
135 php_info_print_table_end();
136 }
137 /* }}} */
138
139 /* {{{ gets and attaches a shared memory segment */
PHP_FUNCTION(shmop_open)140 PHP_FUNCTION(shmop_open)
141 {
142 zend_long key, mode, size;
143 php_shmop *shmop;
144 struct shmid_ds shm;
145 char *flags;
146 size_t flags_len;
147
148 if (zend_parse_parameters(ZEND_NUM_ARGS(), "lsll", &key, &flags, &flags_len, &mode, &size) == FAILURE) {
149 RETURN_THROWS();
150 }
151
152 if (flags_len != 1) {
153 zend_argument_value_error(2, "must be a valid access mode");
154 RETURN_THROWS();
155 }
156
157 object_init_ex(return_value, shmop_ce);
158 shmop = Z_SHMOP_P(return_value);
159 shmop->key = key;
160 shmop->shmflg |= mode;
161
162 switch (flags[0])
163 {
164 case 'a':
165 shmop->shmatflg |= SHM_RDONLY;
166 break;
167 case 'c':
168 shmop->shmflg |= IPC_CREAT;
169 shmop->size = size;
170 break;
171 case 'n':
172 shmop->shmflg |= (IPC_CREAT | IPC_EXCL);
173 shmop->size = size;
174 break;
175 case 'w':
176 /* noop
177 shm segment is being opened for read & write
178 will fail if segment does not exist
179 */
180 break;
181 default:
182 zend_argument_value_error(2, "must be a valid access mode");
183 goto err;
184 }
185
186 if (shmop->shmflg & IPC_CREAT && shmop->size < 1) {
187 zend_argument_value_error(4, "must be greater than 0 for the \"c\" and \"n\" access modes");
188 goto err;
189 }
190
191 shmop->shmid = shmget(shmop->key, shmop->size, shmop->shmflg);
192 if (shmop->shmid == -1) {
193 php_error_docref(NULL, E_WARNING, "Unable to attach or create shared memory segment \"%s\"", strerror(errno));
194 goto err;
195 }
196
197 if (shmctl(shmop->shmid, IPC_STAT, &shm)) {
198 /* please do not add coverage here: the segment would be leaked and impossible to delete via php */
199 php_error_docref(NULL, E_WARNING, "Unable to get shared memory segment information \"%s\"", strerror(errno));
200 goto err;
201 }
202
203 if (shm.shm_segsz > ZEND_LONG_MAX) {
204 zend_argument_value_error(4, "is too large");
205 goto err;
206 }
207
208 shmop->addr = shmat(shmop->shmid, 0, shmop->shmatflg);
209 if (shmop->addr == (char*) -1) {
210 php_error_docref(NULL, E_WARNING, "Unable to attach to shared memory segment \"%s\"", strerror(errno));
211 goto err;
212 }
213
214 shmop->size = shm.shm_segsz;
215 return;
216
217 err:
218 zend_object_release(Z_OBJ_P(return_value));
219 RETURN_FALSE;
220 }
221 /* }}} */
222
223 /* {{{ reads from a shm segment */
PHP_FUNCTION(shmop_read)224 PHP_FUNCTION(shmop_read)
225 {
226 zval *shmid;
227 zend_long start, count;
228 php_shmop *shmop;
229 char *startaddr;
230 int bytes;
231 zend_string *return_string;
232
233 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oll", &shmid, shmop_ce, &start, &count) == FAILURE) {
234 RETURN_THROWS();
235 }
236
237 shmop = Z_SHMOP_P(shmid);
238
239 if (start < 0 || start > shmop->size) {
240 zend_argument_value_error(2, "must be between 0 and the segment size");
241 RETURN_THROWS();
242 }
243
244 if (count < 0 || start > (ZEND_LONG_MAX - count) || start + count > shmop->size) {
245 zend_argument_value_error(3, "is out of range");
246 RETURN_THROWS();
247 }
248
249 startaddr = shmop->addr + start;
250 bytes = count ? count : shmop->size - start;
251
252 return_string = zend_string_init(startaddr, bytes, 0);
253
254 RETURN_NEW_STR(return_string);
255 }
256 /* }}} */
257
258 /* {{{ used to close a shared memory segment; now a NOP */
PHP_FUNCTION(shmop_close)259 PHP_FUNCTION(shmop_close)
260 {
261 zval *shmid;
262
263 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &shmid, shmop_ce) == FAILURE) {
264 RETURN_THROWS();
265 }
266 }
267 /* }}} */
268
269 /* {{{ returns the shm size */
PHP_FUNCTION(shmop_size)270 PHP_FUNCTION(shmop_size)
271 {
272 zval *shmid;
273 php_shmop *shmop;
274
275 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &shmid, shmop_ce) == FAILURE) {
276 RETURN_THROWS();
277 }
278
279 shmop = Z_SHMOP_P(shmid);
280
281 RETURN_LONG(shmop->size);
282 }
283 /* }}} */
284
285 /* {{{ writes to a shared memory segment */
PHP_FUNCTION(shmop_write)286 PHP_FUNCTION(shmop_write)
287 {
288 php_shmop *shmop;
289 zend_long writesize;
290 zend_long offset;
291 zend_string *data;
292 zval *shmid;
293
294 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSl", &shmid, shmop_ce, &data, &offset) == FAILURE) {
295 RETURN_THROWS();
296 }
297
298 shmop = Z_SHMOP_P(shmid);
299
300 if ((shmop->shmatflg & SHM_RDONLY) == SHM_RDONLY) {
301 zend_throw_error(NULL, "Read-only segment cannot be written");
302 RETURN_THROWS();
303 }
304
305 if (offset < 0 || offset > shmop->size) {
306 zend_argument_value_error(3, "is out of range");
307 RETURN_THROWS();
308 }
309
310 writesize = ((zend_long)ZSTR_LEN(data) < shmop->size - offset) ? (zend_long)ZSTR_LEN(data) : shmop->size - offset;
311 memcpy(shmop->addr + offset, ZSTR_VAL(data), writesize);
312
313 RETURN_LONG(writesize);
314 }
315 /* }}} */
316
317 /* {{{ mark segment for deletion */
PHP_FUNCTION(shmop_delete)318 PHP_FUNCTION(shmop_delete)
319 {
320 zval *shmid;
321 php_shmop *shmop;
322
323 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &shmid, shmop_ce) == FAILURE) {
324 RETURN_THROWS();
325 }
326
327 shmop = Z_SHMOP_P(shmid);
328
329 if (shmctl(shmop->shmid, IPC_RMID, NULL)) {
330 php_error_docref(NULL, E_WARNING, "Can't mark segment for deletion (are you the owner?)");
331 RETURN_FALSE;
332 }
333
334 RETURN_TRUE;
335 }
336 /* }}} */
337
338 #endif /* HAVE_SHMOP */
339