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