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