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