xref: /php-src/ext/shmop/shmop.c (revision c8955c07)
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 
88 	return &intern->std;
89 }
90 
shmop_get_constructor(zend_object * object)91 static zend_function *shmop_get_constructor(zend_object *object)
92 {
93 	zend_throw_error(NULL, "Cannot directly construct Shmop, use shmop_open() instead");
94 	return NULL;
95 }
96 
shmop_free_obj(zend_object * object)97 static void shmop_free_obj(zend_object *object)
98 {
99 	php_shmop *shmop = shmop_from_obj(object);
100 
101 	shmdt(shmop->addr);
102 
103 	zend_object_std_dtor(&shmop->std);
104 }
105 
106 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(shmop)107 PHP_MINIT_FUNCTION(shmop)
108 {
109 	shmop_ce = register_class_Shmop();
110 	shmop_ce->create_object = shmop_create_object;
111 	shmop_ce->default_object_handlers = &shmop_object_handlers;
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