xref: /PHP-8.2/ext/posix/posix.c (revision 32efc76c)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Author: Kristian Koehntopp <kris@koehntopp.de>                       |
14    +----------------------------------------------------------------------+
15  */
16 
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20 
21 #include "php.h"
22 #include <unistd.h>
23 #include "ext/standard/info.h"
24 #include "ext/standard/php_string.h"
25 #include "php_posix.h"
26 
27 #ifdef HAVE_POSIX
28 
29 #ifdef HAVE_SYS_TIME_H
30 #include <sys/time.h>
31 #endif
32 
33 #include <sys/resource.h>
34 #include <sys/utsname.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <signal.h>
38 #include <sys/times.h>
39 #include <errno.h>
40 #include <grp.h>
41 #include <pwd.h>
42 #ifdef HAVE_SYS_MKDEV_H
43 # include <sys/mkdev.h>
44 #endif
45 #ifdef HAVE_SYS_SYSMACROS_H
46 # include <sys/sysmacros.h>
47 #endif
48 
49 #include "posix_arginfo.h"
50 
51 ZEND_DECLARE_MODULE_GLOBALS(posix)
52 static PHP_MINFO_FUNCTION(posix);
53 
54 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(posix)55 static PHP_MINFO_FUNCTION(posix)
56 {
57 	php_info_print_table_start();
58 	php_info_print_table_row(2, "POSIX support", "enabled");
59 	php_info_print_table_end();
60 }
61 /* }}} */
62 
PHP_GINIT_FUNCTION(posix)63 static PHP_GINIT_FUNCTION(posix) /* {{{ */
64 {
65 #if defined(COMPILE_DL_POSIX) && defined(ZTS)
66 	ZEND_TSRMLS_CACHE_UPDATE();
67 #endif
68 	posix_globals->last_error = 0;
69 }
70 /* }}} */
71 
72 /* {{{ PHP_MINIT_FUNCTION(posix) */
PHP_MINIT_FUNCTION(posix)73 static PHP_MINIT_FUNCTION(posix)
74 {
75 	register_posix_symbols(module_number);
76 
77 	return SUCCESS;
78 }
79 /* }}} */
80 
81 /* {{{ posix_module_entry */
82 zend_module_entry posix_module_entry = {
83 	STANDARD_MODULE_HEADER,
84 	"posix",
85 	ext_functions,
86 	PHP_MINIT(posix),
87 	NULL,
88 	NULL,
89 	NULL,
90 	PHP_MINFO(posix),
91 	PHP_POSIX_VERSION,
92 	PHP_MODULE_GLOBALS(posix),
93 	PHP_GINIT(posix),
94 	NULL,
95 	NULL,
96 	STANDARD_MODULE_PROPERTIES_EX
97 };
98 /* }}} */
99 
100 #ifdef COMPILE_DL_POSIX
101 #ifdef ZTS
102 ZEND_TSRMLS_CACHE_DEFINE()
103 #endif
ZEND_GET_MODULE(posix)104 ZEND_GET_MODULE(posix)
105 #endif
106 
107 #define PHP_POSIX_RETURN_LONG_FUNC(func_name)	\
108 	ZEND_PARSE_PARAMETERS_NONE();	\
109 	RETURN_LONG(func_name());
110 
111 #define PHP_POSIX_SINGLE_ARG_FUNC(func_name)	\
112 	zend_long val;	\
113 	ZEND_PARSE_PARAMETERS_START(1, 1) \
114 		Z_PARAM_LONG(val) \
115 	ZEND_PARSE_PARAMETERS_END(); \
116 	if (func_name(val) < 0) {	\
117 		POSIX_G(last_error) = errno;	\
118 		RETURN_FALSE;	\
119 	}	\
120 	RETURN_TRUE;
121 
122 /* {{{ Send a signal to a process (POSIX.1, 3.3.2) */
123 
124 PHP_FUNCTION(posix_kill)
125 {
126 	zend_long pid, sig;
127 
128 	ZEND_PARSE_PARAMETERS_START(2, 2)
129 		Z_PARAM_LONG(pid)
130 		Z_PARAM_LONG(sig)
131 	ZEND_PARSE_PARAMETERS_END();
132 
133 	if (kill(pid, sig) < 0) {
134 		POSIX_G(last_error) = errno;
135 		RETURN_FALSE;
136 	}
137 
138 	RETURN_TRUE;
139 }
140 /* }}} */
141 
142 /* {{{ Get the current process id (POSIX.1, 4.1.1) */
PHP_FUNCTION(posix_getpid)143 PHP_FUNCTION(posix_getpid)
144 {
145 	PHP_POSIX_RETURN_LONG_FUNC(getpid);
146 }
147 /* }}} */
148 
149 /* {{{ Get the parent process id (POSIX.1, 4.1.1) */
PHP_FUNCTION(posix_getppid)150 PHP_FUNCTION(posix_getppid)
151 {
152 	PHP_POSIX_RETURN_LONG_FUNC(getppid);
153 }
154 /* }}} */
155 
156 /* {{{ Get the current user id (POSIX.1, 4.2.1) */
PHP_FUNCTION(posix_getuid)157 PHP_FUNCTION(posix_getuid)
158 {
159 	PHP_POSIX_RETURN_LONG_FUNC(getuid);
160 }
161 /* }}} */
162 
163 /* {{{ Get the current group id (POSIX.1, 4.2.1) */
PHP_FUNCTION(posix_getgid)164 PHP_FUNCTION(posix_getgid)
165 {
166 	PHP_POSIX_RETURN_LONG_FUNC(getgid);
167 }
168 /* }}} */
169 
170 /* {{{ Get the current effective user id (POSIX.1, 4.2.1) */
PHP_FUNCTION(posix_geteuid)171 PHP_FUNCTION(posix_geteuid)
172 {
173 	PHP_POSIX_RETURN_LONG_FUNC(geteuid);
174 }
175 /* }}} */
176 
177 /* {{{ Get the current effective group id (POSIX.1, 4.2.1) */
PHP_FUNCTION(posix_getegid)178 PHP_FUNCTION(posix_getegid)
179 {
180 	PHP_POSIX_RETURN_LONG_FUNC(getegid);
181 }
182 /* }}} */
183 
184 /* {{{ Set user id (POSIX.1, 4.2.2) */
PHP_FUNCTION(posix_setuid)185 PHP_FUNCTION(posix_setuid)
186 {
187 	PHP_POSIX_SINGLE_ARG_FUNC(setuid);
188 }
189 /* }}} */
190 
191 /* {{{ Set group id (POSIX.1, 4.2.2) */
PHP_FUNCTION(posix_setgid)192 PHP_FUNCTION(posix_setgid)
193 {
194 	PHP_POSIX_SINGLE_ARG_FUNC(setgid);
195 }
196 /* }}} */
197 
198 /* {{{ Set effective user id */
199 #ifdef HAVE_SETEUID
PHP_FUNCTION(posix_seteuid)200 PHP_FUNCTION(posix_seteuid)
201 {
202 	PHP_POSIX_SINGLE_ARG_FUNC(seteuid);
203 }
204 #endif
205 /* }}} */
206 
207 /* {{{ Set effective group id */
208 #ifdef HAVE_SETEGID
PHP_FUNCTION(posix_setegid)209 PHP_FUNCTION(posix_setegid)
210 {
211 	PHP_POSIX_SINGLE_ARG_FUNC(setegid);
212 }
213 #endif
214 /* }}} */
215 
216 /* {{{ Get supplementary group id's (POSIX.1, 4.2.3) */
217 #ifdef HAVE_GETGROUPS
PHP_FUNCTION(posix_getgroups)218 PHP_FUNCTION(posix_getgroups)
219 {
220 	gid_t *gidlist;
221 	int    result;
222 	int    i;
223 
224 	ZEND_PARSE_PARAMETERS_NONE();
225 
226 	/* MacOS may return more than NGROUPS_MAX groups.
227 	 * Fetch the actual number of groups and create an appropriate allocation. */
228 	if ((result = getgroups(0, NULL)) < 0) {
229 		POSIX_G(last_error) = errno;
230 		RETURN_FALSE;
231 	}
232 
233 	gidlist = emalloc(sizeof(gid_t) * result);
234 	if ((result = getgroups(result, gidlist)) < 0) {
235 		POSIX_G(last_error) = errno;
236 		efree(gidlist);
237 		RETURN_FALSE;
238 	}
239 
240 	array_init(return_value);
241 
242 	for (i=0; i<result; i++) {
243 		add_next_index_long(return_value, gidlist[i]);
244 	}
245 	efree(gidlist);
246 }
247 #endif
248 /* }}} */
249 
250 /* {{{ Get user name (POSIX.1, 4.2.4) */
251 #ifdef HAVE_GETLOGIN
PHP_FUNCTION(posix_getlogin)252 PHP_FUNCTION(posix_getlogin)
253 {
254 	char *p;
255 
256 	ZEND_PARSE_PARAMETERS_NONE();
257 
258 	if (NULL == (p = getlogin())) {
259 		POSIX_G(last_error) = errno;
260 		RETURN_FALSE;
261 	}
262 
263 	RETURN_STRING(p);
264 }
265 #endif
266 /* }}} */
267 
268 /* {{{ Get current process group id (POSIX.1, 4.3.1) */
PHP_FUNCTION(posix_getpgrp)269 PHP_FUNCTION(posix_getpgrp)
270 {
271 	PHP_POSIX_RETURN_LONG_FUNC(getpgrp);
272 }
273 /* }}} */
274 
275 /* {{{ Create session and set process group id (POSIX.1, 4.3.2) */
276 #ifdef HAVE_SETSID
PHP_FUNCTION(posix_setsid)277 PHP_FUNCTION(posix_setsid)
278 {
279 	PHP_POSIX_RETURN_LONG_FUNC(setsid);
280 }
281 #endif
282 /* }}} */
283 
284 /* {{{ Set process group id for job control (POSIX.1, 4.3.3) */
PHP_FUNCTION(posix_setpgid)285 PHP_FUNCTION(posix_setpgid)
286 {
287 	zend_long pid, pgid;
288 
289 	ZEND_PARSE_PARAMETERS_START(2, 2)
290 		Z_PARAM_LONG(pid)
291 		Z_PARAM_LONG(pgid)
292 	ZEND_PARSE_PARAMETERS_END();
293 
294 	if (setpgid(pid, pgid) < 0) {
295 		POSIX_G(last_error) = errno;
296 		RETURN_FALSE;
297 	}
298 
299 	RETURN_TRUE;
300 }
301 /* }}} */
302 
303 /* {{{ Get the process group id of the specified process (This is not a POSIX function, but a SVR4ism, so we compile conditionally) */
304 #ifdef HAVE_GETPGID
PHP_FUNCTION(posix_getpgid)305 PHP_FUNCTION(posix_getpgid)
306 {
307 	zend_long val;
308 
309 	ZEND_PARSE_PARAMETERS_START(1, 1)
310 		Z_PARAM_LONG(val)
311 	ZEND_PARSE_PARAMETERS_END();
312 
313 	if ((val = getpgid(val)) < 0) {
314 		POSIX_G(last_error) = errno;
315 		RETURN_FALSE;
316 	}
317 	RETURN_LONG(val);
318 }
319 #endif
320 /* }}} */
321 
322 /* {{{ Get process group id of session leader (This is not a POSIX function, but a SVR4ism, so be compile conditionally) */
323 #ifdef HAVE_GETSID
PHP_FUNCTION(posix_getsid)324 PHP_FUNCTION(posix_getsid)
325 {
326 	zend_long val;
327 
328 	ZEND_PARSE_PARAMETERS_START(1, 1)
329 		Z_PARAM_LONG(val)
330 	ZEND_PARSE_PARAMETERS_END();
331 
332 	if ((val = getsid(val)) < 0) {
333 		POSIX_G(last_error) = errno;
334 		RETURN_FALSE;
335 	}
336 	RETURN_LONG(val);
337 }
338 #endif
339 /* }}} */
340 
341 /* {{{ Get system name (POSIX.1, 4.4.1) */
PHP_FUNCTION(posix_uname)342 PHP_FUNCTION(posix_uname)
343 {
344 	struct utsname u;
345 
346 	ZEND_PARSE_PARAMETERS_NONE();
347 
348 	if (uname(&u) < 0) {
349 		POSIX_G(last_error) = errno;
350 		RETURN_FALSE;
351 	}
352 
353 	array_init(return_value);
354 
355 	add_assoc_string(return_value, "sysname",  u.sysname);
356 	add_assoc_string(return_value, "nodename", u.nodename);
357 	add_assoc_string(return_value, "release",  u.release);
358 	add_assoc_string(return_value, "version",  u.version);
359 	add_assoc_string(return_value, "machine",  u.machine);
360 
361 #if defined(_GNU_SOURCE) && !defined(DARWIN) && defined(HAVE_UTSNAME_DOMAINNAME)
362 	add_assoc_string(return_value, "domainname", u.domainname);
363 #endif
364 }
365 /* }}} */
366 
367 /* POSIX.1, 4.5.1 time() - Get System Time
368 							already covered by PHP
369  */
370 
371 /* {{{ Get process times (POSIX.1, 4.5.2) */
PHP_FUNCTION(posix_times)372 PHP_FUNCTION(posix_times)
373 {
374 	struct tms t;
375 	clock_t    ticks;
376 
377 	ZEND_PARSE_PARAMETERS_NONE();
378 
379 	if ((ticks = times(&t)) == -1) {
380 		POSIX_G(last_error) = errno;
381 		RETURN_FALSE;
382 	}
383 
384 	array_init(return_value);
385 
386 	add_assoc_long(return_value, "ticks",	ticks);			/* clock ticks */
387 	add_assoc_long(return_value, "utime",	t.tms_utime);	/* user time */
388 	add_assoc_long(return_value, "stime",	t.tms_stime);	/* system time */
389 	add_assoc_long(return_value, "cutime",	t.tms_cutime);	/* user time of children */
390 	add_assoc_long(return_value, "cstime",	t.tms_cstime);	/* system time of children */
391 }
392 /* }}} */
393 
394 /* POSIX.1, 4.6.1 getenv() - Environment Access
395 							already covered by PHP
396 */
397 
398 /* {{{ Generate terminal path name (POSIX.1, 4.7.1) */
399 #ifdef HAVE_CTERMID
PHP_FUNCTION(posix_ctermid)400 PHP_FUNCTION(posix_ctermid)
401 {
402 	char  buffer[L_ctermid];
403 
404 	ZEND_PARSE_PARAMETERS_NONE();
405 
406 	if (NULL == ctermid(buffer)) {
407 		/* Found no documentation how the defined behaviour is when this
408 		 * function fails
409 		 */
410 		POSIX_G(last_error) = errno;
411 		RETURN_FALSE;
412 	}
413 
414 	RETURN_STRING(buffer);
415 }
416 #endif
417 /* }}} */
418 
419 /* Checks if the provides resource is a stream and if it provides a file descriptor */
php_posix_stream_get_fd(zval * zfp,int * fd)420 static int php_posix_stream_get_fd(zval *zfp, int *fd) /* {{{ */
421 {
422 	php_stream *stream;
423 
424 	php_stream_from_zval_no_verify(stream, zfp);
425 
426 	if (stream == NULL) {
427 		return 0;
428 	}
429 	if (php_stream_can_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT) == SUCCESS) {
430 		php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT, (void*)fd, 0);
431 	} else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD) == SUCCESS) {
432 		php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)fd, 0);
433 	} else {
434 		php_error_docref(NULL, E_WARNING, "Could not use stream of type '%s'",
435 				stream->ops->label);
436 		return 0;
437 	}
438 	return 1;
439 }
440 /* }}} */
441 
442 /* {{{ Determine terminal device name (POSIX.1, 4.7.2) */
PHP_FUNCTION(posix_ttyname)443 PHP_FUNCTION(posix_ttyname)
444 {
445 	zval *z_fd;
446 	char *p;
447 	int fd;
448 #if defined(ZTS) && defined(HAVE_TTYNAME_R) && defined(_SC_TTY_NAME_MAX)
449 	zend_long buflen;
450 	int err;
451 #endif
452 
453 	ZEND_PARSE_PARAMETERS_START(1, 1)
454 		Z_PARAM_ZVAL(z_fd)
455 	ZEND_PARSE_PARAMETERS_END();
456 
457 	switch (Z_TYPE_P(z_fd)) {
458 		case IS_RESOURCE:
459 			if (!php_posix_stream_get_fd(z_fd, &fd)) {
460 				RETURN_FALSE;
461 			}
462 			break;
463 		default:
464 			fd = zval_get_long(z_fd);
465 	}
466 #if defined(ZTS) && defined(HAVE_TTYNAME_R) && defined(_SC_TTY_NAME_MAX)
467 	buflen = sysconf(_SC_TTY_NAME_MAX);
468 	if (buflen < 1) {
469 		buflen = 32;
470 	}
471 #if ZEND_DEBUG
472 	/* Test retry logic */
473 	buflen = 1;
474 #endif
475 	p = emalloc(buflen);
476 
477 try_again:
478 	err = ttyname_r(fd, p, buflen);
479 	if (err) {
480 		if (err == ERANGE) {
481 			buflen *= 2;
482 			p = erealloc(p, buflen);
483 			goto try_again;
484 		}
485 		POSIX_G(last_error) = err;
486 		efree(p);
487 		RETURN_FALSE;
488 	}
489 	RETVAL_STRING(p);
490 	efree(p);
491 #else
492 	if (NULL == (p = ttyname(fd))) {
493 		POSIX_G(last_error) = errno;
494 		RETURN_FALSE;
495 	}
496 	RETURN_STRING(p);
497 #endif
498 }
499 /* }}} */
500 
501 /* {{{ Determine if filedesc is a tty (POSIX.1, 4.7.1) */
PHP_FUNCTION(posix_isatty)502 PHP_FUNCTION(posix_isatty)
503 {
504 	zval *z_fd;
505 	int fd;
506 
507 	ZEND_PARSE_PARAMETERS_START(1, 1)
508 		Z_PARAM_ZVAL(z_fd)
509 	ZEND_PARSE_PARAMETERS_END();
510 
511 	switch (Z_TYPE_P(z_fd)) {
512 		case IS_RESOURCE:
513 			if (!php_posix_stream_get_fd(z_fd, &fd)) {
514 				RETURN_FALSE;
515 			}
516 			break;
517 		default:
518 			fd = zval_get_long(z_fd);
519 	}
520 
521 	if (isatty(fd)) {
522 		RETURN_TRUE;
523 	} else {
524 		RETURN_FALSE;
525 	}
526 }
527 /* }}} */
528 
529 /*
530 	POSIX.1, 4.8.1 sysconf() - TODO
531 	POSIX.1, 5.7.1 pathconf(), fpathconf() - TODO
532 
533 	POSIX.1, 5.1.2 opendir(), readdir(), rewinddir(), closedir()
534 	POSIX.1, 5.2.1 chdir()
535 				already supported by PHP
536  */
537 
538 /* {{{ Get working directory pathname (POSIX.1, 5.2.2) */
PHP_FUNCTION(posix_getcwd)539 PHP_FUNCTION(posix_getcwd)
540 {
541 	char  buffer[MAXPATHLEN];
542 	char *p;
543 
544 	ZEND_PARSE_PARAMETERS_NONE();
545 
546 	p = VCWD_GETCWD(buffer, MAXPATHLEN);
547 	if (!p) {
548 		POSIX_G(last_error) = errno;
549 		RETURN_FALSE;
550 	}
551 
552 	RETURN_STRING(buffer);
553 }
554 /* }}} */
555 
556 /*
557 	POSIX.1, 5.3.x open(), creat(), umask()
558 	POSIX.1, 5.4.1 link()
559 		already supported by PHP.
560  */
561 
562 /* {{{ Make a FIFO special file (POSIX.1, 5.4.2) */
563 #ifdef HAVE_MKFIFO
PHP_FUNCTION(posix_mkfifo)564 PHP_FUNCTION(posix_mkfifo)
565 {
566 	zend_string *path;
567 	zend_long mode;
568 	int     result;
569 
570 	ZEND_PARSE_PARAMETERS_START(2, 2)
571 		Z_PARAM_PATH_STR(path)
572 		Z_PARAM_LONG(mode)
573 	ZEND_PARSE_PARAMETERS_END();
574 
575 	if (php_check_open_basedir_ex(ZSTR_VAL(path), 0)) {
576 		RETURN_FALSE;
577 	}
578 
579 	result = mkfifo(ZSTR_VAL(path), mode);
580 	if (result < 0) {
581 		POSIX_G(last_error) = errno;
582 		RETURN_FALSE;
583 	}
584 
585 	RETURN_TRUE;
586 }
587 #endif
588 /* }}} */
589 
590 /* {{{ Make a special or ordinary file (POSIX.1) */
591 #ifdef HAVE_MKNOD
PHP_FUNCTION(posix_mknod)592 PHP_FUNCTION(posix_mknod)
593 {
594 	zend_string *path;
595 	zend_long mode;
596 	zend_long major = 0, minor = 0;
597 	int result;
598 	dev_t php_dev = 0;
599 
600 	ZEND_PARSE_PARAMETERS_START(2, 4)
601 		Z_PARAM_PATH_STR(path)
602 		Z_PARAM_LONG(mode)
603 		Z_PARAM_OPTIONAL
604 		Z_PARAM_LONG(major)
605 		Z_PARAM_LONG(minor)
606 	ZEND_PARSE_PARAMETERS_END();
607 
608 	if (php_check_open_basedir_ex(ZSTR_VAL(path), 0)) {
609 		RETURN_FALSE;
610 	}
611 
612 	if ((mode & S_IFCHR) || (mode & S_IFBLK)) {
613 		if (major == 0) {
614 			zend_argument_value_error(3, "cannot be 0 for the POSIX_S_IFCHR and POSIX_S_IFBLK modes");
615 			RETURN_THROWS();
616 		} else {
617 #if defined(HAVE_MAKEDEV) || defined(makedev)
618 			php_dev = makedev(major, minor);
619 #else
620 			php_error_docref(NULL, E_WARNING, "Cannot create a block or character device, creating a normal file instead");
621 #endif
622 		}
623 	}
624 
625 	result = mknod(ZSTR_VAL(path), mode, php_dev);
626 	if (result < 0) {
627 		POSIX_G(last_error) = errno;
628 		RETURN_FALSE;
629 	}
630 
631 	RETURN_TRUE;
632 }
633 #endif
634 /* }}} */
635 
636 /* Takes a pointer to posix group and a pointer to an already initialized ZVAL
637  * array container and fills the array with the posix group member data. */
php_posix_group_to_array(struct group * g,zval * array_group)638 int php_posix_group_to_array(struct group *g, zval *array_group) /* {{{ */
639 {
640 	zval array_members;
641 	int count;
642 
643 	if (NULL == g)
644 		return 0;
645 
646 	if (array_group == NULL || Z_TYPE_P(array_group) != IS_ARRAY)
647 		return 0;
648 
649 	array_init(&array_members);
650 
651 	add_assoc_string(array_group, "name", g->gr_name);
652 	if (g->gr_passwd) {
653 		add_assoc_string(array_group, "passwd", g->gr_passwd);
654 	} else {
655 		add_assoc_null(array_group, "passwd");
656 	}
657 	for (count = 0;; count++) {
658 		/* gr_mem entries may be misaligned on macos. */
659 		char *gr_mem;
660 		memcpy(&gr_mem, &g->gr_mem[count], sizeof(char *));
661 		if (!gr_mem) {
662 			break;
663 		}
664 
665 		add_next_index_string(&array_members, gr_mem);
666 	}
667 	zend_hash_str_update(Z_ARRVAL_P(array_group), "members", sizeof("members")-1, &array_members);
668 	add_assoc_long(array_group, "gid", g->gr_gid);
669 	return 1;
670 }
671 /* }}} */
672 
673 /*
674 	POSIX.1, 5.5.1 unlink()
675 	POSIX.1, 5.5.2 rmdir()
676 	POSIX.1, 5.5.3 rename()
677 	POSIX.1, 5.6.x stat(), chmod(), utime() already supported by PHP.
678 */
679 
680 /* {{{ Determine accessibility of a file (POSIX.1 5.6.3) */
PHP_FUNCTION(posix_access)681 PHP_FUNCTION(posix_access)
682 {
683 	zend_long mode = 0;
684 	size_t filename_len, ret;
685 	char *filename, *path;
686 
687 	ZEND_PARSE_PARAMETERS_START(1, 2)
688 		Z_PARAM_PATH(filename, filename_len)
689 		Z_PARAM_OPTIONAL
690 		Z_PARAM_LONG(mode)
691 	ZEND_PARSE_PARAMETERS_END();
692 
693 	path = expand_filepath(filename, NULL);
694 	if (!path) {
695 		POSIX_G(last_error) = EIO;
696 		RETURN_FALSE;
697 	}
698 
699 	if (php_check_open_basedir_ex(path, 0)) {
700 		efree(path);
701 		POSIX_G(last_error) = EPERM;
702 		RETURN_FALSE;
703 	}
704 
705 	ret = access(path, mode);
706 	efree(path);
707 
708 	if (ret) {
709 		POSIX_G(last_error) = errno;
710 		RETURN_FALSE;
711 	}
712 
713 	RETURN_TRUE;
714 }
715 /* }}} */
716 
717 /*
718 	POSIX.1, 6.x most I/O functions already supported by PHP.
719 	POSIX.1, 7.x tty functions, TODO
720 	POSIX.1, 8.x interactions with other C language functions
721 	POSIX.1, 9.x system database access
722 */
723 
724 /* {{{ Group database access (POSIX.1, 9.2.1) */
PHP_FUNCTION(posix_getgrnam)725 PHP_FUNCTION(posix_getgrnam)
726 {
727 	char *name;
728 	struct group *g;
729 	size_t name_len;
730 #if defined(ZTS) && defined(HAVE_GETGRNAM_R) && defined(_SC_GETGR_R_SIZE_MAX)
731 	struct group gbuf;
732 	long buflen;
733 	char *buf;
734 	int err;
735 #endif
736 
737 	ZEND_PARSE_PARAMETERS_START(1, 1)
738 		Z_PARAM_STRING(name, name_len)
739 	ZEND_PARSE_PARAMETERS_END();
740 
741 #if defined(ZTS) && defined(HAVE_GETGRNAM_R) && defined(_SC_GETGR_R_SIZE_MAX)
742 	buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
743 	if (buflen < 1) {
744 		buflen = 1024;
745 	}
746 #if ZEND_DEBUG
747 	/* Test retry logic */
748 	buflen = 1;
749 #endif
750 	buf = emalloc(buflen);
751 try_again:
752 	g = &gbuf;
753 
754 	err = getgrnam_r(name, g, buf, buflen, &g);
755 	if (err || g == NULL) {
756 		if (err == ERANGE) {
757 			buflen *= 2;
758 			buf = erealloc(buf, buflen);
759 			goto try_again;
760 		}
761 		POSIX_G(last_error) = err;
762 		efree(buf);
763 		RETURN_FALSE;
764 	}
765 #else
766 	if (NULL == (g = getgrnam(name))) {
767 		POSIX_G(last_error) = errno;
768 		RETURN_FALSE;
769 	}
770 #endif
771 	array_init(return_value);
772 
773 	if (!php_posix_group_to_array(g, return_value)) {
774 		zend_array_destroy(Z_ARR_P(return_value));
775 		php_error_docref(NULL, E_WARNING, "Unable to convert posix group to array");
776 		RETVAL_FALSE;
777 	}
778 #if defined(ZTS) && defined(HAVE_GETGRNAM_R) && defined(_SC_GETGR_R_SIZE_MAX)
779 	efree(buf);
780 #endif
781 }
782 /* }}} */
783 
784 /* {{{ Group database access (POSIX.1, 9.2.1) */
PHP_FUNCTION(posix_getgrgid)785 PHP_FUNCTION(posix_getgrgid)
786 {
787 	zend_long gid;
788 #if defined(ZTS) && defined(HAVE_GETGRGID_R) && defined(_SC_GETGR_R_SIZE_MAX)
789 	int err;
790 	struct group _g;
791 	struct group *retgrptr = NULL;
792 	long grbuflen;
793 	char *grbuf;
794 #endif
795 	struct group *g;
796 
797 	ZEND_PARSE_PARAMETERS_START(1, 1)
798 		Z_PARAM_LONG(gid)
799 	ZEND_PARSE_PARAMETERS_END();
800 
801 #if defined(ZTS) && defined(HAVE_GETGRGID_R) && defined(_SC_GETGR_R_SIZE_MAX)
802 
803 	grbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
804 	if (grbuflen < 1) {
805 		grbuflen = 1024;
806 	}
807 #if ZEND_DEBUG
808 	/* Test retry logic */
809 	grbuflen = 1;
810 #endif
811 
812 	grbuf = emalloc(grbuflen);
813 
814 try_again:
815 	err = getgrgid_r(gid, &_g, grbuf, grbuflen, &retgrptr);
816 	if (err || retgrptr == NULL) {
817 		if (err == ERANGE) {
818 			grbuflen *= 2;
819 			grbuf = erealloc(grbuf, grbuflen);
820 			goto try_again;
821 		}
822 		POSIX_G(last_error) = err;
823 		efree(grbuf);
824 		RETURN_FALSE;
825 	}
826 	g = &_g;
827 #else
828 	if (NULL == (g = getgrgid(gid))) {
829 		POSIX_G(last_error) = errno;
830 		RETURN_FALSE;
831 	}
832 #endif
833 	array_init(return_value);
834 
835 	if (!php_posix_group_to_array(g, return_value)) {
836 		zend_array_destroy(Z_ARR_P(return_value));
837 		php_error_docref(NULL, E_WARNING, "Unable to convert posix group struct to array");
838 		RETVAL_FALSE;
839 	}
840 #if defined(ZTS) && defined(HAVE_GETGRGID_R) && defined(_SC_GETGR_R_SIZE_MAX)
841 	efree(grbuf);
842 #endif
843 }
844 /* }}} */
845 
php_posix_passwd_to_array(struct passwd * pw,zval * return_value)846 int php_posix_passwd_to_array(struct passwd *pw, zval *return_value) /* {{{ */
847 {
848 	if (NULL == pw)
849 		return 0;
850 	if (NULL == return_value || Z_TYPE_P(return_value) != IS_ARRAY)
851 		return 0;
852 
853 	add_assoc_string(return_value, "name",      pw->pw_name);
854 	add_assoc_string(return_value, "passwd",    pw->pw_passwd);
855 	add_assoc_long  (return_value, "uid",       pw->pw_uid);
856 	add_assoc_long  (return_value, "gid",		pw->pw_gid);
857 	add_assoc_string(return_value, "gecos",     pw->pw_gecos);
858 	add_assoc_string(return_value, "dir",       pw->pw_dir);
859 	add_assoc_string(return_value, "shell",     pw->pw_shell);
860 	return 1;
861 }
862 /* }}} */
863 
864 /* {{{ User database access (POSIX.1, 9.2.2) */
PHP_FUNCTION(posix_getpwnam)865 PHP_FUNCTION(posix_getpwnam)
866 {
867 	struct passwd *pw;
868 	char *name;
869 	size_t name_len;
870 #if defined(ZTS) && defined(_SC_GETPW_R_SIZE_MAX) && defined(HAVE_GETPWNAM_R)
871 	struct passwd pwbuf;
872 	long buflen;
873 	char *buf;
874 	int err;
875 #endif
876 
877 	ZEND_PARSE_PARAMETERS_START(1, 1)
878 		Z_PARAM_STRING(name, name_len)
879 	ZEND_PARSE_PARAMETERS_END();
880 
881 #if defined(ZTS) && defined(_SC_GETPW_R_SIZE_MAX) && defined(HAVE_GETPWNAM_R)
882 	buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
883 	if (buflen < 1) {
884 		buflen = 1024;
885 	}
886 #if ZEND_DEBUG
887 	/* Test retry logic */
888 	buflen = 1;
889 #endif
890 	buf = emalloc(buflen);
891 
892 try_again:
893 	pw = &pwbuf;
894 	err = getpwnam_r(name, pw, buf, buflen, &pw);
895 	if (err || pw == NULL) {
896 		if (err == ERANGE) {
897 			buflen *= 2;
898 			buf = erealloc(buf, buflen);
899 			goto try_again;
900 		}
901 		efree(buf);
902 		POSIX_G(last_error) = err;
903 		RETURN_FALSE;
904 	}
905 #else
906 	if (NULL == (pw = getpwnam(name))) {
907 		POSIX_G(last_error) = errno;
908 		RETURN_FALSE;
909 	}
910 #endif
911 	array_init(return_value);
912 
913 	if (!php_posix_passwd_to_array(pw, return_value)) {
914 		zend_array_destroy(Z_ARR_P(return_value));
915 		php_error_docref(NULL, E_WARNING, "Unable to convert posix passwd struct to array");
916 		RETVAL_FALSE;
917 	}
918 #if defined(ZTS) && defined(_SC_GETPW_R_SIZE_MAX) && defined(HAVE_GETPWNAM_R)
919 	efree(buf);
920 #endif
921 }
922 /* }}} */
923 
924 /* {{{ User database access (POSIX.1, 9.2.2) */
PHP_FUNCTION(posix_getpwuid)925 PHP_FUNCTION(posix_getpwuid)
926 {
927 	zend_long uid;
928 #if defined(ZTS) && defined(_SC_GETPW_R_SIZE_MAX) && defined(HAVE_GETPWUID_R)
929 	struct passwd _pw;
930 	struct passwd *retpwptr = NULL;
931 	long pwbuflen;
932 	char *pwbuf;
933 	int err;
934 #endif
935 	struct passwd *pw;
936 
937 	ZEND_PARSE_PARAMETERS_START(1, 1)
938 		Z_PARAM_LONG(uid)
939 	ZEND_PARSE_PARAMETERS_END();
940 
941 #if defined(ZTS) && defined(_SC_GETPW_R_SIZE_MAX) && defined(HAVE_GETPWUID_R)
942 	pwbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
943 	if (pwbuflen < 1) {
944 		pwbuflen = 1024;
945 	}
946 #if ZEND_DEBUG
947 	/* Test retry logic */
948 	pwbuflen = 1;
949 #endif
950 	pwbuf = emalloc(pwbuflen);
951 
952 try_again:
953 	err = getpwuid_r(uid, &_pw, pwbuf, pwbuflen, &retpwptr);
954 	if (err || retpwptr == NULL) {
955 		if (err == ERANGE) {
956 			pwbuflen *= 2;
957 			pwbuf = erealloc(pwbuf, pwbuflen);
958 			goto try_again;
959 		}
960 		POSIX_G(last_error) = err;
961 		efree(pwbuf);
962 		RETURN_FALSE;
963 	}
964 	pw = &_pw;
965 #else
966 	if (NULL == (pw = getpwuid(uid))) {
967 		POSIX_G(last_error) = errno;
968 		RETURN_FALSE;
969 	}
970 #endif
971 	array_init(return_value);
972 
973 	if (!php_posix_passwd_to_array(pw, return_value)) {
974 		zend_array_destroy(Z_ARR_P(return_value));
975 		php_error_docref(NULL, E_WARNING, "Unable to convert posix passwd struct to array");
976 		RETVAL_FALSE;
977 	}
978 #if defined(ZTS) && defined(_SC_GETPW_R_SIZE_MAX) && defined(HAVE_GETPWUID_R)
979 	efree(pwbuf);
980 #endif
981 }
982 /* }}} */
983 
984 
985 #ifdef HAVE_GETRLIMIT
986 
987 #define UNLIMITED_STRING "unlimited"
988 
989 /* {{{ posix_addlimit */
posix_addlimit(int limit,const char * name,zval * return_value)990 static int posix_addlimit(int limit, const char *name, zval *return_value) {
991 	int result;
992 	struct rlimit rl;
993 	char hard[80];
994 	char soft[80];
995 
996 	snprintf(hard, 80, "hard %s", name);
997 	snprintf(soft, 80, "soft %s", name);
998 
999 	result = getrlimit(limit, &rl);
1000 	if (result < 0) {
1001 		POSIX_G(last_error) = errno;
1002 		return FAILURE;
1003 	}
1004 
1005 	if (rl.rlim_cur == RLIM_INFINITY) {
1006 		add_assoc_stringl(return_value, soft, UNLIMITED_STRING, sizeof(UNLIMITED_STRING)-1);
1007 	} else {
1008 		add_assoc_long(return_value, soft, rl.rlim_cur);
1009 	}
1010 
1011 	if (rl.rlim_max == RLIM_INFINITY) {
1012 		add_assoc_stringl(return_value, hard, UNLIMITED_STRING, sizeof(UNLIMITED_STRING)-1);
1013 	} else {
1014 		add_assoc_long(return_value, hard, rl.rlim_max);
1015 	}
1016 
1017 	return SUCCESS;
1018 }
1019 /* }}} */
1020 
1021 /* {{{ limits[] */
1022 static const struct limitlist {
1023 	int limit;
1024 	const char *name;
1025 } limits[] = {
1026 #ifdef RLIMIT_CORE
1027 	{ RLIMIT_CORE,	"core" },
1028 #endif
1029 
1030 #ifdef RLIMIT_DATA
1031 	{ RLIMIT_DATA,	"data" },
1032 #endif
1033 
1034 #ifdef RLIMIT_STACK
1035 	{ RLIMIT_STACK,	"stack" },
1036 #endif
1037 
1038 #ifdef RLIMIT_VMEM
1039 	{ RLIMIT_VMEM, "virtualmem" },
1040 #endif
1041 
1042 #ifdef RLIMIT_AS
1043 	{ RLIMIT_AS, "totalmem" },
1044 #endif
1045 
1046 #ifdef RLIMIT_RSS
1047 	{ RLIMIT_RSS, "rss" },
1048 #endif
1049 
1050 #ifdef RLIMIT_NPROC
1051 	{ RLIMIT_NPROC, "maxproc" },
1052 #endif
1053 
1054 #ifdef RLIMIT_MEMLOCK
1055 	{ RLIMIT_MEMLOCK, "memlock" },
1056 #endif
1057 
1058 #ifdef RLIMIT_CPU
1059 	{ RLIMIT_CPU,	"cpu" },
1060 #endif
1061 
1062 #ifdef RLIMIT_FSIZE
1063 	{ RLIMIT_FSIZE, "filesize" },
1064 #endif
1065 
1066 #ifdef RLIMIT_NOFILE
1067 	{ RLIMIT_NOFILE, "openfiles" },
1068 #endif
1069 
1070 #ifdef RLIMIT_OFILE
1071 	{ RLIMIT_OFILE, "openfiles" },
1072 #endif
1073 
1074 #ifdef RLIMIT_KQUEUES
1075 	{ RLIMIT_KQUEUES, "kqueues" },
1076 #endif
1077 
1078 #ifdef RLIMIT_NPTS
1079 	{ RLIMIT_NPTS, "npts" },
1080 #endif
1081 
1082 	{ 0, NULL }
1083 };
1084 /* }}} */
1085 
1086 
1087 /* {{{ Get system resource consumption limits (This is not a POSIX function, but a BSDism and a SVR4ism. We compile conditionally) */
PHP_FUNCTION(posix_getrlimit)1088 PHP_FUNCTION(posix_getrlimit)
1089 {
1090 	const struct limitlist *l = NULL;
1091 
1092 	ZEND_PARSE_PARAMETERS_NONE();
1093 
1094 	array_init(return_value);
1095 
1096 	for (l=limits; l->name; l++) {
1097 		if (posix_addlimit(l->limit, l->name, return_value) == FAILURE) {
1098 			zend_array_destroy(Z_ARR_P(return_value));
1099 			RETURN_FALSE;
1100 		}
1101 	}
1102 }
1103 /* }}} */
1104 
1105 #endif /* HAVE_GETRLIMIT */
1106 
1107 #ifdef HAVE_SETRLIMIT
1108 /* {{{ Set system resource consumption limits (POSIX.1-2001) */
PHP_FUNCTION(posix_setrlimit)1109 PHP_FUNCTION(posix_setrlimit)
1110 {
1111 	struct rlimit rl;
1112 	zend_long res, cur, max;
1113 
1114 	ZEND_PARSE_PARAMETERS_START(3, 3)
1115 		Z_PARAM_LONG(res)
1116 		Z_PARAM_LONG(cur)
1117 		Z_PARAM_LONG(max)
1118 	ZEND_PARSE_PARAMETERS_END();
1119 
1120 	rl.rlim_cur = cur;
1121 	rl.rlim_max = max;
1122 
1123 	if (setrlimit(res, &rl) == -1) {
1124 		POSIX_G(last_error) = errno;
1125 		RETURN_FALSE;
1126 	}
1127 
1128 	RETURN_TRUE;
1129 }
1130 /* }}} */
1131 
1132 #endif /* HAVE_SETRLIMIT */
1133 
1134 
1135 /* {{{ Retrieve the error number set by the last posix function which failed. */
PHP_FUNCTION(posix_get_last_error)1136 PHP_FUNCTION(posix_get_last_error)
1137 {
1138 	ZEND_PARSE_PARAMETERS_NONE();
1139 
1140 	RETURN_LONG(POSIX_G(last_error));
1141 }
1142 /* }}} */
1143 
1144 /* {{{ Retrieve the system error message associated with the given errno. */
PHP_FUNCTION(posix_strerror)1145 PHP_FUNCTION(posix_strerror)
1146 {
1147 	zend_long error;
1148 
1149 	ZEND_PARSE_PARAMETERS_START(1, 1)
1150 		Z_PARAM_LONG(error)
1151 	ZEND_PARSE_PARAMETERS_END();
1152 
1153 	RETURN_STRING(strerror(error));
1154 }
1155 /* }}} */
1156 
1157 #endif
1158 
1159 #ifdef HAVE_INITGROUPS
1160 /* {{{ Calculate the group access list for the user specified in name. */
PHP_FUNCTION(posix_initgroups)1161 PHP_FUNCTION(posix_initgroups)
1162 {
1163 	zend_long basegid;
1164 	char *name;
1165 	size_t name_len;
1166 
1167 	ZEND_PARSE_PARAMETERS_START(2, 2)
1168 		Z_PARAM_STRING(name, name_len)
1169 		Z_PARAM_LONG(basegid)
1170 	ZEND_PARSE_PARAMETERS_END();
1171 
1172 	if (name_len == 0) {
1173 		RETURN_FALSE;
1174 	}
1175 
1176 	RETURN_BOOL(!initgroups((const char *)name, basegid));
1177 }
1178 /* }}} */
1179 #endif
1180