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