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 | https://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_shmop.h"
26 #include "Zend/zend_attributes.h"
27
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
89 return &intern->std;
90 }
91
shmop_get_constructor(zend_object * object)92 static zend_function *shmop_get_constructor(zend_object *object)
93 {
94 zend_throw_error(NULL, "Cannot directly construct Shmop, use shmop_open() instead");
95 return NULL;
96 }
97
shmop_free_obj(zend_object * object)98 static void shmop_free_obj(zend_object *object)
99 {
100 php_shmop *shmop = shmop_from_obj(object);
101
102 shmdt(shmop->addr);
103
104 zend_object_std_dtor(&shmop->std);
105 }
106
107 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(shmop)108 PHP_MINIT_FUNCTION(shmop)
109 {
110 shmop_ce = register_class_Shmop();
111 shmop_ce->create_object = shmop_create_object;
112 shmop_ce->default_object_handlers = &shmop_object_handlers;
113
114 memcpy(&shmop_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
115 shmop_object_handlers.offset = XtOffsetOf(php_shmop, std);
116 shmop_object_handlers.free_obj = shmop_free_obj;
117 shmop_object_handlers.get_constructor = shmop_get_constructor;
118 shmop_object_handlers.clone_obj = NULL;
119 shmop_object_handlers.compare = zend_objects_not_comparable;
120
121 register_shmop_symbols(module_number);
122
123 return SUCCESS;
124 }
125 /* }}} */
126
127 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(shmop)128 PHP_MINFO_FUNCTION(shmop)
129 {
130 php_info_print_table_start();
131 php_info_print_table_row(2, "shmop support", "enabled");
132 php_info_print_table_end();
133 }
134 /* }}} */
135
136 /* {{{ gets and attaches a shared memory segment */
PHP_FUNCTION(shmop_open)137 PHP_FUNCTION(shmop_open)
138 {
139 zend_long key, mode, size;
140 php_shmop *shmop;
141 struct shmid_ds shm;
142 char *flags;
143 size_t flags_len;
144
145 if (zend_parse_parameters(ZEND_NUM_ARGS(), "lsll", &key, &flags, &flags_len, &mode, &size) == FAILURE) {
146 RETURN_THROWS();
147 }
148
149 if (flags_len != 1) {
150 zend_argument_value_error(2, "must be a valid access mode");
151 RETURN_THROWS();
152 }
153
154 object_init_ex(return_value, shmop_ce);
155 shmop = Z_SHMOP_P(return_value);
156 shmop->key = key;
157 shmop->shmflg |= mode;
158
159 switch (flags[0])
160 {
161 case 'a':
162 shmop->shmatflg |= SHM_RDONLY;
163 break;
164 case 'c':
165 shmop->shmflg |= IPC_CREAT;
166 shmop->size = size;
167 break;
168 case 'n':
169 shmop->shmflg |= (IPC_CREAT | IPC_EXCL);
170 shmop->size = size;
171 break;
172 case 'w':
173 /* noop
174 shm segment is being opened for read & write
175 will fail if segment does not exist
176 */
177 break;
178 default:
179 zend_argument_value_error(2, "must be a valid access mode");
180 goto err;
181 }
182
183 if (shmop->shmflg & IPC_CREAT && shmop->size < 1) {
184 zend_argument_value_error(4, "must be greater than 0 for the \"c\" and \"n\" access modes");
185 goto err;
186 }
187
188 shmop->shmid = shmget(shmop->key, shmop->size, shmop->shmflg);
189 if (shmop->shmid == -1) {
190 php_error_docref(NULL, E_WARNING, "Unable to attach or create shared memory segment \"%s\"", strerror(errno));
191 goto err;
192 }
193
194 if (shmctl(shmop->shmid, IPC_STAT, &shm)) {
195 /* please do not add coverage here: the segment would be leaked and impossible to delete via php */
196 php_error_docref(NULL, E_WARNING, "Unable to get shared memory segment information \"%s\"", strerror(errno));
197 goto err;
198 }
199
200 if (shm.shm_segsz > ZEND_LONG_MAX) {
201 zend_argument_value_error(4, "is too large");
202 goto err;
203 }
204
205 shmop->addr = shmat(shmop->shmid, 0, shmop->shmatflg);
206 if (shmop->addr == (char*) -1) {
207 php_error_docref(NULL, E_WARNING, "Unable to attach to shared memory segment \"%s\"", strerror(errno));
208 goto err;
209 }
210
211 shmop->size = shm.shm_segsz;
212 return;
213
214 err:
215 zend_object_release(Z_OBJ_P(return_value));
216 RETURN_FALSE;
217 }
218 /* }}} */
219
220 /* {{{ reads from a shm segment */
PHP_FUNCTION(shmop_read)221 PHP_FUNCTION(shmop_read)
222 {
223 zval *shmid;
224 zend_long start, count;
225 php_shmop *shmop;
226 char *startaddr;
227 int bytes;
228 zend_string *return_string;
229
230 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oll", &shmid, shmop_ce, &start, &count) == FAILURE) {
231 RETURN_THROWS();
232 }
233
234 shmop = Z_SHMOP_P(shmid);
235
236 if (start < 0 || start > shmop->size) {
237 zend_argument_value_error(2, "must be between 0 and the segment size");
238 RETURN_THROWS();
239 }
240
241 if (count < 0 || start > (ZEND_LONG_MAX - count) || start + count > shmop->size) {
242 zend_argument_value_error(3, "is out of range");
243 RETURN_THROWS();
244 }
245
246 startaddr = shmop->addr + start;
247 bytes = count ? count : shmop->size - start;
248
249 return_string = zend_string_init(startaddr, bytes, 0);
250
251 RETURN_NEW_STR(return_string);
252 }
253 /* }}} */
254
255 /* {{{ used to close a shared memory segment; now a NOP */
PHP_FUNCTION(shmop_close)256 PHP_FUNCTION(shmop_close)
257 {
258 zval *shmid;
259
260 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &shmid, shmop_ce) == FAILURE) {
261 RETURN_THROWS();
262 }
263 }
264 /* }}} */
265
266 /* {{{ returns the shm size */
PHP_FUNCTION(shmop_size)267 PHP_FUNCTION(shmop_size)
268 {
269 zval *shmid;
270 php_shmop *shmop;
271
272 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &shmid, shmop_ce) == FAILURE) {
273 RETURN_THROWS();
274 }
275
276 shmop = Z_SHMOP_P(shmid);
277
278 RETURN_LONG(shmop->size);
279 }
280 /* }}} */
281
282 /* {{{ writes to a shared memory segment */
PHP_FUNCTION(shmop_write)283 PHP_FUNCTION(shmop_write)
284 {
285 php_shmop *shmop;
286 zend_long writesize;
287 zend_long offset;
288 zend_string *data;
289 zval *shmid;
290
291 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSl", &shmid, shmop_ce, &data, &offset) == FAILURE) {
292 RETURN_THROWS();
293 }
294
295 shmop = Z_SHMOP_P(shmid);
296
297 if ((shmop->shmatflg & SHM_RDONLY) == SHM_RDONLY) {
298 zend_throw_error(NULL, "Read-only segment cannot be written");
299 RETURN_THROWS();
300 }
301
302 if (offset < 0 || offset > shmop->size) {
303 zend_argument_value_error(3, "is out of range");
304 RETURN_THROWS();
305 }
306
307 writesize = ((zend_long)ZSTR_LEN(data) < shmop->size - offset) ? (zend_long)ZSTR_LEN(data) : shmop->size - offset;
308 memcpy(shmop->addr + offset, ZSTR_VAL(data), writesize);
309
310 RETURN_LONG(writesize);
311 }
312 /* }}} */
313
314 /* {{{ mark segment for deletion */
PHP_FUNCTION(shmop_delete)315 PHP_FUNCTION(shmop_delete)
316 {
317 zval *shmid;
318 php_shmop *shmop;
319
320 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &shmid, shmop_ce) == FAILURE) {
321 RETURN_THROWS();
322 }
323
324 shmop = Z_SHMOP_P(shmid);
325
326 if (shmctl(shmop->shmid, IPC_RMID, NULL)) {
327 php_error_docref(NULL, E_WARNING, "Can't mark segment for deletion (are you the owner?)");
328 RETURN_FALSE;
329 }
330
331 RETURN_TRUE;
332 }
333 /* }}} */
334
335 #endif /* HAVE_SHMOP */
336