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 "php_shmop.h"
27 # ifndef PHP_WIN32
28 # include <sys/ipc.h>
29 # include <sys/shm.h>
30 #else
31 #include "tsrm_win32.h"
32 #endif
33
34
35 #if HAVE_SHMOP
36
37 #include "ext/standard/info.h"
38
39 #ifdef ZTS
40 int shmop_globals_id;
41 #else
42 php_shmop_globals shmop_globals;
43 #endif
44
45 int shm_type;
46
47 /* {{{ arginfo */
48 ZEND_BEGIN_ARG_INFO_EX(arginfo_shmop_open, 0, 0, 4)
49 ZEND_ARG_INFO(0, key)
50 ZEND_ARG_INFO(0, flags)
51 ZEND_ARG_INFO(0, mode)
52 ZEND_ARG_INFO(0, size)
53 ZEND_END_ARG_INFO()
54
55 ZEND_BEGIN_ARG_INFO_EX(arginfo_shmop_read, 0, 0, 3)
56 ZEND_ARG_INFO(0, shmid)
57 ZEND_ARG_INFO(0, start)
58 ZEND_ARG_INFO(0, count)
59 ZEND_END_ARG_INFO()
60
61 ZEND_BEGIN_ARG_INFO_EX(arginfo_shmop_close, 0, 0, 1)
62 ZEND_ARG_INFO(0, shmid)
63 ZEND_END_ARG_INFO()
64
65 ZEND_BEGIN_ARG_INFO_EX(arginfo_shmop_size, 0, 0, 1)
66 ZEND_ARG_INFO(0, shmid)
67 ZEND_END_ARG_INFO()
68
69 ZEND_BEGIN_ARG_INFO_EX(arginfo_shmop_write, 0, 0, 3)
70 ZEND_ARG_INFO(0, shmid)
71 ZEND_ARG_INFO(0, data)
72 ZEND_ARG_INFO(0, offset)
73 ZEND_END_ARG_INFO()
74
75 ZEND_BEGIN_ARG_INFO_EX(arginfo_shmop_delete, 0, 0, 1)
76 ZEND_ARG_INFO(0, shmid)
77 ZEND_END_ARG_INFO()
78 /* }}} */
79
80 /* {{{ shmop_functions[]
81 */
82 static const zend_function_entry shmop_functions[] = {
83 PHP_FE(shmop_open, arginfo_shmop_open)
84 PHP_FE(shmop_read, arginfo_shmop_read)
85 PHP_FE(shmop_close, arginfo_shmop_close)
86 PHP_FE(shmop_size, arginfo_shmop_size)
87 PHP_FE(shmop_write, arginfo_shmop_write)
88 PHP_FE(shmop_delete, arginfo_shmop_delete)
89 PHP_FE_END
90 };
91 /* }}} */
92
93 /* {{{ shmop_module_entry
94 */
95 zend_module_entry shmop_module_entry = {
96 STANDARD_MODULE_HEADER,
97 "shmop",
98 shmop_functions,
99 PHP_MINIT(shmop),
100 NULL,
101 NULL,
102 NULL,
103 PHP_MINFO(shmop),
104 PHP_SHMOP_VERSION,
105 STANDARD_MODULE_PROPERTIES
106 };
107 /* }}} */
108
109 #ifdef COMPILE_DL_SHMOP
ZEND_GET_MODULE(shmop)110 ZEND_GET_MODULE(shmop)
111 #endif
112
113 /* {{{ rsclean
114 */
115 static void rsclean(zend_resource *rsrc)
116 {
117 struct php_shmop *shmop = (struct php_shmop *)rsrc->ptr;
118
119 shmdt(shmop->addr);
120 efree(shmop);
121 }
122 /* }}} */
123
124 /* {{{ PHP_MINIT_FUNCTION
125 */
PHP_MINIT_FUNCTION(shmop)126 PHP_MINIT_FUNCTION(shmop)
127 {
128 shm_type = zend_register_list_destructors_ex(rsclean, NULL, "shmop", module_number);
129
130 return SUCCESS;
131 }
132 /* }}} */
133
134 /* {{{ PHP_MINFO_FUNCTION
135 */
PHP_MINFO_FUNCTION(shmop)136 PHP_MINFO_FUNCTION(shmop)
137 {
138 php_info_print_table_start();
139 php_info_print_table_row(2, "shmop support", "enabled");
140 php_info_print_table_end();
141 }
142 /* }}} */
143
144 /* {{{ proto resource shmop_open(int key, string flags, int mode, int size)
145 gets and attaches a shared memory segment */
PHP_FUNCTION(shmop_open)146 PHP_FUNCTION(shmop_open)
147 {
148 zend_long key, mode, size;
149 struct php_shmop *shmop;
150 struct shmid_ds shm;
151 char *flags;
152 size_t flags_len;
153
154 if (zend_parse_parameters(ZEND_NUM_ARGS(), "lsll", &key, &flags, &flags_len, &mode, &size) == FAILURE) {
155 return;
156 }
157
158 if (flags_len != 1) {
159 php_error_docref(NULL, E_WARNING, "%s is not a valid flag", flags);
160 RETURN_FALSE;
161 }
162
163 shmop = emalloc(sizeof(struct php_shmop));
164 memset(shmop, 0, sizeof(struct php_shmop));
165
166 shmop->key = key;
167 shmop->shmflg |= mode;
168
169 switch (flags[0])
170 {
171 case 'a':
172 shmop->shmatflg |= SHM_RDONLY;
173 break;
174 case 'c':
175 shmop->shmflg |= IPC_CREAT;
176 shmop->size = size;
177 break;
178 case 'n':
179 shmop->shmflg |= (IPC_CREAT | IPC_EXCL);
180 shmop->size = size;
181 break;
182 case 'w':
183 /* noop
184 shm segment is being opened for read & write
185 will fail if segment does not exist
186 */
187 break;
188 default:
189 php_error_docref(NULL, E_WARNING, "invalid access mode");
190 goto err;
191 }
192
193 if (shmop->shmflg & IPC_CREAT && shmop->size < 1) {
194 php_error_docref(NULL, E_WARNING, "Shared memory segment size must be greater than zero");
195 goto err;
196 }
197
198 shmop->shmid = shmget(shmop->key, shmop->size, shmop->shmflg);
199 if (shmop->shmid == -1) {
200 php_error_docref(NULL, E_WARNING, "unable to attach or create shared memory segment '%s'", strerror(errno));
201 goto err;
202 }
203
204 if (shmctl(shmop->shmid, IPC_STAT, &shm)) {
205 /* please do not add coverage here: the segment would be leaked and impossible to delete via php */
206 php_error_docref(NULL, E_WARNING, "unable to get shared memory segment information '%s'", strerror(errno));
207 goto err;
208 }
209
210 if (shm.shm_segsz > ZEND_LONG_MAX) {
211 php_error_docref(NULL, E_WARNING, "shared memory segment too large to attach");
212 goto err;
213 }
214
215 shmop->addr = shmat(shmop->shmid, 0, shmop->shmatflg);
216 if (shmop->addr == (char*) -1) {
217 php_error_docref(NULL, E_WARNING, "unable to attach to shared memory segment '%s'", strerror(errno));
218 goto err;
219 }
220
221 shmop->size = shm.shm_segsz;
222
223 RETURN_RES(zend_register_resource(shmop, shm_type));
224 err:
225 efree(shmop);
226 RETURN_FALSE;
227 }
228 /* }}} */
229
230 /* {{{ proto string shmop_read(resource shmid, int start, int count)
231 reads from a shm segment */
PHP_FUNCTION(shmop_read)232 PHP_FUNCTION(shmop_read)
233 {
234 zval *shmid;
235 zend_long start, count;
236 struct php_shmop *shmop;
237 char *startaddr;
238 int bytes;
239 zend_string *return_string;
240
241 if (zend_parse_parameters(ZEND_NUM_ARGS(), "rll", &shmid, &start, &count) == FAILURE) {
242 return;
243 }
244
245 if ((shmop = (struct php_shmop *)zend_fetch_resource(Z_RES_P(shmid), "shmop", shm_type)) == NULL) {
246 RETURN_FALSE;
247 }
248
249 if (start < 0 || start > shmop->size) {
250 php_error_docref(NULL, E_WARNING, "start is out of range");
251 RETURN_FALSE;
252 }
253
254 if (count < 0 || start > (ZEND_LONG_MAX - count) || start + count > shmop->size) {
255 php_error_docref(NULL, E_WARNING, "count is out of range");
256 RETURN_FALSE;
257 }
258
259 startaddr = shmop->addr + start;
260 bytes = count ? count : shmop->size - start;
261
262 return_string = zend_string_init(startaddr, bytes, 0);
263
264 RETURN_NEW_STR(return_string);
265 }
266 /* }}} */
267
268 /* {{{ proto void shmop_close(resource shmid)
269 closes a shared memory segment */
PHP_FUNCTION(shmop_close)270 PHP_FUNCTION(shmop_close)
271 {
272 zval *shmid;
273 struct php_shmop *shmop;
274
275 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &shmid) == FAILURE) {
276 return;
277 }
278
279
280 if ((shmop = (struct php_shmop *)zend_fetch_resource(Z_RES_P(shmid), "shmop", shm_type)) == NULL) {
281 RETURN_FALSE;
282 }
283
284 zend_list_close(Z_RES_P(shmid));
285 }
286 /* }}} */
287
288 /* {{{ proto int shmop_size(resource shmid)
289 returns the shm size */
PHP_FUNCTION(shmop_size)290 PHP_FUNCTION(shmop_size)
291 {
292 zval *shmid;
293 struct php_shmop *shmop;
294
295 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &shmid) == FAILURE) {
296 return;
297 }
298
299 if ((shmop = (struct php_shmop *)zend_fetch_resource(Z_RES_P(shmid), "shmop", shm_type)) == NULL) {
300 RETURN_FALSE;
301 }
302
303 RETURN_LONG(shmop->size);
304 }
305 /* }}} */
306
307 /* {{{ proto int shmop_write(resource shmid, string data, int offset)
308 writes to a shared memory segment */
PHP_FUNCTION(shmop_write)309 PHP_FUNCTION(shmop_write)
310 {
311 struct php_shmop *shmop;
312 zend_long writesize;
313 zend_long offset;
314 zend_string *data;
315 zval *shmid;
316
317 if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSl", &shmid, &data, &offset) == FAILURE) {
318 return;
319 }
320
321 if ((shmop = (struct php_shmop *)zend_fetch_resource(Z_RES_P(shmid), "shmop", shm_type)) == NULL) {
322 RETURN_FALSE;
323 }
324
325 if ((shmop->shmatflg & SHM_RDONLY) == SHM_RDONLY) {
326 php_error_docref(NULL, E_WARNING, "trying to write to a read only segment");
327 RETURN_FALSE;
328 }
329
330 if (offset < 0 || offset > shmop->size) {
331 php_error_docref(NULL, E_WARNING, "offset out of range");
332 RETURN_FALSE;
333 }
334
335 writesize = ((zend_long)ZSTR_LEN(data) < shmop->size - offset) ? (zend_long)ZSTR_LEN(data) : shmop->size - offset;
336 memcpy(shmop->addr + offset, ZSTR_VAL(data), writesize);
337
338 RETURN_LONG(writesize);
339 }
340 /* }}} */
341
342 /* {{{ proto bool shmop_delete(resource shmid)
343 mark segment for deletion */
PHP_FUNCTION(shmop_delete)344 PHP_FUNCTION(shmop_delete)
345 {
346 zval *shmid;
347 struct php_shmop *shmop;
348
349 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &shmid) == FAILURE) {
350 return;
351 }
352
353 if ((shmop = (struct php_shmop *)zend_fetch_resource(Z_RES_P(shmid), "shmop", shm_type)) == NULL) {
354 RETURN_FALSE;
355 }
356
357 if (shmctl(shmop->shmid, IPC_RMID, NULL)) {
358 php_error_docref(NULL, E_WARNING, "can't mark segment for deletion (are you the owner?)");
359 RETURN_FALSE;
360 }
361
362 RETURN_TRUE;
363 }
364 /* }}} */
365
366 #endif /* HAVE_SHMOP */
367