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