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
110 ZEND_GET_MODULE(shmop)
111 #endif
112 
113 /* {{{ rsclean
114  */
rsclean(zend_resource *rsrc)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_FUNCTIONnull126 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_FUNCTIONnull136 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_FUNCTIONnull146 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_FUNCTIONnull232 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 > (INT_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_FUNCTIONnull270 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_FUNCTIONnull290 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_FUNCTIONnull309 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_FUNCTIONnull344 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