Try the invalidate_fileno() idea for non-leaky stdio sockets.
Nick Ing-Simmons [Tue, 21 Jan 2003 20:22:55 +0000 (20:22 +0000)]
p4raw-id: //depot/perlio@18539

perlio.c

index 2d5785b..996b893 100644 (file)
--- a/perlio.c
+++ b/perlio.c
@@ -2684,13 +2684,86 @@ PerlIOStdio_dup(pTHX_ PerlIO *f, PerlIO *o, CLONE_PARAMS *param, int flags)
     return f;
 }
 
+static int
+PerlIOStdio_invalidate_fileno(pTHX_ FILE *f)
+{
+    /* XXX this could use PerlIO_canset_fileno() and
+     * PerlIO_set_fileno() support from Configure 
+     */
+#  if defined(__GLIBC__)
+    /* There may be a better way for GLIBC:
+       - libio.h defines a flag to not close() on cleanup 
+     */        
+    f->_fileno = -1;
+    return 1;
+#  elif defined(__sun__)
+#    if defined(_LP64)
+    /* On solaris, if _LP64 is defined, the FILE structure is this:
+     *
+     *  struct FILE {
+     *      long __pad[16];
+     *  };
+     *
+     * It turns out that the fd is stored in the top 32 bits of 
+     * file->__pad[4]. The lower 32 bits contain flags. file->pad[5] appears
+     * to contain a pointer or offset into another structure. All the
+     * remaining fields are zero.
+     *
+     * We set the top bits to -1 (0xFFFFFFFF).
+     */
+    f->__pad[4] |= 0xffffffff00000000L;
+    assert(fileno(f) == 0xffffffff);
+#    else /* !defined(_LP64) */
+    /* _file is just a unsigned char :-( 
+       Not clear why we dup() rather than using -1 
+       even if that would be treated as 0xFF - so will 
+       a dup fail ...
+     */
+    f->_file = PerlLIO_dup(fd);
+#    endif /* defined(_LP64) */
+    return 1;
+#  elif defined(__hpux)
+    f->__fileH = 0xff;
+    f->__fileL = 0xff;
+    return 1;
+#  elif defined(_AIX)
+    f->_file = -1;
+    return 1;
+#  elif defined(__FreeBSD__)
+    /* There may be a better way on FreeBSD:
+        - we could insert a dummy func in the _close function entry 
+       f->_close = (int (*)(void *)) dummy_close; 
+     */
+    f->_file = -1;
+    return 1;
+#  elif defined(__CYGWIN__)
+    /* There may be a better way on CYGWIN:
+        - we could insert a dummy func in the _close function entry 
+       f->_close = (int (*)(void *)) dummy_close; 
+     */
+    f->_file = -1;
+    return 1;
+#  elif defined(WIN32)
+#    if defined(__BORLANDC__)
+    f->fd = PerlLIO_dup(fileno(f));
+#    else
+    f->_file = -1;
+#    endif
+    return 1;
+#  else
+#if 0
+    /* Sarathy's code did this - we fall back to a dup/dup2 hack 
+       (which isn't thread safe) instead
+     */  
+#    error "Don't know how to set FILE.fileno on your platform"
+#endif
+    return 0;
+#  endif
+}
+
 IV
 PerlIOStdio_close(pTHX_ PerlIO *f)
 {
-#ifdef SOCKS5_VERSION_NAME
-    int optval;
-    Sock_size_t optlen = sizeof(int);
-#endif
     FILE *stdio = PerlIOSelf(f, PerlIOStdio)->stdio;
     if (!stdio) {
        errno = EBADF;
@@ -2698,52 +2771,66 @@ PerlIOStdio_close(pTHX_ PerlIO *f)
     }
     else {
         int fd = fileno(stdio);
-       int dupfd = -1;
+       int socksfd = 0;
+       int invalidate = 0;
        IV result;
+       int saveerr = 0;
+       int dupfd = 0;
+#ifdef SOCKS5_VERSION_NAME
+       /* Socks lib overrides close() but stdio isn't linked to 
+          that library (though we are) - so we must call close() 
+          on sockets on stdio's behalf. 
+        */   
+       int optval;
+       Sock_size_t optlen = sizeof(int);
+       if (getsockopt(fd, SOL_SOCKET, SO_TYPE, (void *) &optval, &optlen) == 0) {
+            socksfd = 1;
+           invalidate = 1;  
+       }
+#endif
        if (PerlIOUnix_refcnt_dec(fd) > 0) {
            /* File descriptor still in use */
-           if (fd < 3) {
-               /* For STD* handles don't close the stdio at all */
+           invalidate = 1;
+           socksfd = 0;
+       }    
+       if (invalidate) {
+           /* For STD* handles don't close the stdio at all 
+              this is because we have shared the FILE * too 
+            */
+           if (stdio == stdin) {
+               /* Some stdios are buggy fflush-ing inputs */
+               return 0;
+           }
+           else if (stdio == stdout || stdio == stderr) {
                return PerlIO_flush(f);
            }
-           else {
-               /* Tricky - must fclose(stdio) to free memory but not close(fd) */ 
-#ifdef USE_THREADS
-               /* Sarathy pointed out that another thread could reuse
-                  fd after fclose() but before we dup2() below
-                  so take out a MUTEX to shut them out 
-                */  
-               MUTEX_LOCK(&PerlIO_mutex);
-#endif
+            /* Tricky - must fclose(stdio) to free memory but not close(fd) 
+              Use Sarathy's trick from maint-5.6 to invalidate the 
+              fileno slot of the FILE * 
+           */ 
+           saveerr = errno;
+           if (!(invalidate = PerlIOStdio_invalidate_fileno(aTHX_ stdio))) {
                dupfd = PerlLIO_dup(fd);
            }
-       }    
-        result = (
-#ifdef SOCKS5_VERSION_NAME
-              (getsockopt
-               (PerlIO_fileno(f), SOL_SOCKET, SO_TYPE, (void *) &optval,
-                &optlen) <
-               0) ? PerlSIO_fclose(stdio) : close(PerlIO_fileno(f))
-#else
-              PerlSIO_fclose(stdio)
-#endif
-       );
-       if (dupfd >= 0) {
-           /* We need to restore fd from the saved copy */
-           if (PerlLIO_dup2(dupfd,fd) != fd)
-             result = -1;
-#ifdef USE_THREADS
-           MUTEX_UNLOCK(&PerlIO_mutex);
-#endif
-           if (PerlLIO_close(dupfd) != 0)
-             result = -1; 
+       } 
+        result = PerlSIO_fclose(stdio);
+       /* We treat EBADF from stdio as success if we invalidated */
+       if (invalidate && result != 0 && errno == EBADF) {
+           errno = saveerr;
+           result = 0;
+       } 
+       if (socksfd) {
+           /* in SOCKS case let close() determine return value */
+           result = close(fd);
+       }
+       if (dupfd) {
+           PerlLIO_dup2(dupfd,fd);
+           close(dupfd);
        }
        return result;
     } 
 }
 
-
-
 SSize_t
 PerlIOStdio_read(pTHX_ PerlIO *f, void *vbuf, Size_t count)
 {