Skip to content

Commit e04f587

Browse files
committed
Fix pipe detection and stream position handling
There are two related changes here: 1. Also check for S_ISCHR when checking for pipes on linux, otherwise we will not detect ttys. 2. Always set position=-1 (i.e. ftell will return false) when a pipe is detected. Previously position=0 was sometimes used, depending on whether we're on Windows/Linux and whether the FD or FILE codepath was used.
1 parent a2c21e1 commit e04f587

File tree

2 files changed

+50
-31
lines changed

2 files changed

+50
-31
lines changed

main/streams/plain_wrapper.c

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -242,35 +242,36 @@ PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC)
242242
return php_stream_fopen_temporary_file(NULL, "php", NULL);
243243
}
244244

245+
static void detect_is_pipe(php_stdio_stream_data *self) {
246+
#if defined(S_ISFIFO) && defined(S_ISCHR)
247+
if (self->fd >= 0 && do_fstat(self, 0) == 0) {
248+
self->is_pipe = S_ISFIFO(self->sb.st_mode) || S_ISCHR(self->sb.st_mode);
249+
}
250+
#elif defined(PHP_WIN32)
251+
zend_uintptr_t handle = _get_osfhandle(self->fd);
252+
253+
if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) {
254+
self->is_pipe = GetFileType((HANDLE)handle) == FILE_TYPE_PIPE;
255+
}
256+
#endif
257+
}
258+
245259
PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id STREAMS_DC)
246260
{
247261
php_stream *stream = php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id);
248262

249263
if (stream) {
250264
php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
251265

252-
#ifdef S_ISFIFO
253-
/* detect if this is a pipe */
254-
if (self->fd >= 0) {
255-
self->is_pipe = (do_fstat(self, 0) == 0 && S_ISFIFO(self->sb.st_mode)) ? 1 : 0;
256-
}
257-
#elif defined(PHP_WIN32)
258-
{
259-
zend_uintptr_t handle = _get_osfhandle(self->fd);
260-
261-
if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) {
262-
self->is_pipe = GetFileType((HANDLE)handle) == FILE_TYPE_PIPE;
263-
}
264-
}
265-
#endif
266-
266+
detect_is_pipe(self);
267267
if (self->is_pipe) {
268268
stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
269+
stream->position = -1;
269270
} else {
270271
stream->position = zend_lseek(self->fd, 0, SEEK_CUR);
271272
#ifdef ESPIPE
273+
/* FIXME: Is this code still needed? */
272274
if (stream->position == (zend_off_t)-1 && errno == ESPIPE) {
273-
stream->position = 0;
274275
stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
275276
self->is_pipe = 1;
276277
}
@@ -288,23 +289,10 @@ PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STRE
288289
if (stream) {
289290
php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
290291

291-
#ifdef S_ISFIFO
292-
/* detect if this is a pipe */
293-
if (self->fd >= 0) {
294-
self->is_pipe = (do_fstat(self, 0) == 0 && S_ISFIFO(self->sb.st_mode)) ? 1 : 0;
295-
}
296-
#elif defined(PHP_WIN32)
297-
{
298-
zend_uintptr_t handle = _get_osfhandle(self->fd);
299-
300-
if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) {
301-
self->is_pipe = GetFileType((HANDLE)handle) == FILE_TYPE_PIPE;
302-
}
303-
}
304-
#endif
305-
292+
detect_is_pipe(self);
306293
if (self->is_pipe) {
307294
stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
295+
stream->position = -1;
308296
} else {
309297
stream->position = zend_ftell(file);
310298
}

sapi/cli/tests/std_streams.phpt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Testing ftell() on std streams
3+
--SKIPIF--
4+
<?php
5+
if (getenv("SKIP_IO_CAPTURE_TESTS")) {
6+
die("skip I/O capture test");
7+
}
8+
?>
9+
--CAPTURE_STDIO--
10+
STDOUT
11+
--FILE--
12+
<?php
13+
14+
// These have proc_open pipes attached
15+
var_dump(ftell(STDIN));
16+
var_dump(ftell(STDERR));
17+
var_dump(ftell(fopen("php://stdin", "r")));
18+
var_dump(ftell(fopen("php://stderr", "w")));
19+
20+
// These have a tty attached
21+
var_dump(ftell(STDOUT));
22+
var_dump(ftell(fopen("php://stdout", "w")));
23+
24+
?>
25+
--EXPECT--
26+
bool(false)
27+
bool(false)
28+
bool(false)
29+
bool(false)
30+
bool(false)
31+
bool(false)

0 commit comments

Comments
 (0)