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