Reputation: 389
This issue is more or less related to embedded perl in C, perlapio - interoperability with STDIO which I think I have solved for the Windows environment. I will post a complete solution if this new issue is solved too.
In the linked question,
StoryTeller gave me the hint
to use PerlIO_findFILE()
which solved the immediate problem, but the same code on Linux behaves strangely.
Perl's dup2()
seems to have a different behaviour on Win32, where dup2()
is a macro for win32_dup2()
, which as far as I understand simply uses the dup2()
from io.h
.
On Win32, Perl's version returns zero on success and non-zero on error, but on Linux the default ANSI dup2()
will be used, which instead returns the new file descriptor. Then, I'll have to check errno
if everything went fine.
If a call to PerlIO_findFILE()
sets errno
to "illegal seek" (errno 29 - ESPIPE
), then after dup
, dup2
, pipe
etc. errno
is still set to "illegal seek", and any further checks on errno
still see the same error.
(In practice everything has worked for me because there was no actual error. Also, the solution by checking errno
is not thread safe, since between the syscall and the check another tread may reset errno.)
Note that I have
#define PERLIO_NOT_STDIO 0
in effect and I'm using Perl5.14.1.
Am I doing something really wrong here?
Here's a simplified code snippet:
stdOutFILE = PerlIO_findFILE(PerlIO_stderr()); // convert Perl's stdout to stdio FILE handle
fdStdOutOriginal = fileno(stdOutFILE); // get descriptor
if ( fdStdOutOriginal >= 0 ) {
relocatedStdOut = dup(fdStdOutOriginal); // relocate stdOut for external writing
if ( relocatedStdOut >= 0 )
{
if ( pipe(fdPipeStdOut) == 0 ) // create pipe for forwarding to stderr
{
// this has to be done on win32:
// if ( dup2(fdPipeStdOut[1], fdStdOutOriginal) == 0 ) // hang pipe on stdOut
dup2(fdPipeStdOut[1], fdStdOutOriginal);
if( errno == 0 ) {
// do some funny stuff
} else {
// report error
}
}
}
}
Upvotes: 2
Views: 419
Reputation: 385897
errno
is meaningless unless a C library call or a system call reports an error, so it can't be used to determine if an error occurred. Notably, these calls aren't required to (and usually don't) reset errno
on success. It's not even safe to clear errno
before the call, because the call may set errno
even if no error occurred.
As best as I can tell, Perl's emulation of dup2
returns the same value as the POSIX one (-1
on error, newfd
on success).
#ifndef HAS_DUP2
int
dup2(int oldfd, int newfd)
{
#if defined(HAS_FCNTL) && defined(F_DUPFD)
if (oldfd == newfd)
return oldfd;
PerlLIO_close(newfd);
return fcntl(oldfd, F_DUPFD, newfd);
#else
#define DUP2_MAX_FDS 256
int fdtmp[DUP2_MAX_FDS];
I32 fdx = 0;
int fd;
if (oldfd == newfd)
return oldfd;
PerlLIO_close(newfd);
/* good enough for low fd's... */
while ((fd = PerlLIO_dup(oldfd)) != newfd && fd >= 0) {
if (fdx >= DUP2_MAX_FDS) {
PerlLIO_close(fd);
fd = -1;
break;
}
fdtmp[fdx++] = fd;
}
while (fdx > 0)
PerlLIO_close(fdtmp[--fdx]);
return fd;
#endif
}
#endif
(From 5.24.1)
This means that we can detect an error in a platform-independent fashion, despite your claim to the contrary. As such, the proper usage is
if ( dup2(fdPipeStdOut[1], fdStdOutOriginal) >= 0 ) {
//do some funny stuff
} else {
//report error
}
Upvotes: 3