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