Avoid throwing exceptions during early boot if stdin is closed
authorAndy Wingo <wingo@pobox.com>
Fri, 13 Dec 2019 12:52:58 +0000 (13:52 +0100)
committerAndy Wingo <wingo@pobox.com>
Fri, 13 Dec 2019 12:52:58 +0000 (13:52 +0100)
* libguile/fports.c (scm_i_fdes_is_valid): New internal helper.
  (scm_i_fdes_to_port): Use new helper.
* libguile/fports.h: Declare new helper.
* libguile/init.c (scm_standard_stream_to_port): Refactor to use
  scm_i_fdes_is_valid.

libguile/fports.c
libguile/fports.h
libguile/init.c

index d97c54c016f60c742e407cb5c5c471a85efa27d1..6019d9ec3d7e24c95e0423db5b661e5323760ec8 100644 (file)
@@ -401,6 +401,29 @@ SCM_DEFINE (scm_i_open_file, "open-file", 2, 0, 1,
 \f
 /* Building Guile ports from a file descriptor.  */
 
+int
+scm_i_fdes_is_valid (int fdes, long mode_bits)
+{
+#ifdef F_GETFL
+  int flags = fcntl (fdes, F_GETFL, 0);
+  if (flags == -1)
+    return 0;
+  flags &= O_ACCMODE;
+  if (flags == O_RDWR)
+    return 1;
+  if (flags != O_WRONLY && (mode_bits & SCM_WRTNG))
+    return 0;
+  if (flags != O_RDONLY && (mode_bits & SCM_RDNG))
+    return 0;
+  return 1;
+#else
+  /* If we don't have F_GETFL, as on mingw, at least we can test that
+     it is a valid file descriptor.  */
+  struct stat st;
+  return fstat (fdes, &st) == 0;
+#endif
+}
+
 /* Build a Scheme port from an open file descriptor `fdes'.
    MODE indicates whether FILE is open for reading or writing; it uses
       the same notation as open-file's second argument.
@@ -415,27 +438,14 @@ scm_i_fdes_to_port (int fdes, long mode_bits, SCM name, unsigned options)
 
   if (options & SCM_FPORT_OPTION_VERIFY)
     {
-      /* Check that the foreign FD is valid and matches the mode
-         bits.  */
-#ifdef F_GETFL
-      int flags = fcntl (fdes, F_GETFL, 0);
-      if (flags == -1)
-        SCM_SYSERROR;
-      flags &= O_ACCMODE;
-      if (flags != O_RDWR
-          && ((flags != O_WRONLY && (mode_bits & SCM_WRTNG))
-              || (flags != O_RDONLY && (mode_bits & SCM_RDNG))))
+      errno = 0;
+      if (!scm_i_fdes_is_valid (fdes, mode_bits))
         {
+          if (errno)
+            SCM_SYSERROR;
           SCM_MISC_ERROR ("requested file mode not available on fdes",
                           SCM_EOL);
         }
-#else
-      /* If we don't have F_GETFL, as on mingw, at least we can test that
-         it is a valid file descriptor.  */
-      struct stat st;
-      if (fstat (fdes, &st) != 0)
-        SCM_SYSERROR;
-#endif
     }
 
   fp = (scm_t_fport *) scm_gc_malloc_pointerless (sizeof (scm_t_fport),
index fd5b86d96d7d89349a9adb8b3fccc7857d3f6af3..3a895775fda03e58ebfc710113e728f12586a904 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef SCM_FPORTS_H
 #define SCM_FPORTS_H
 
-/* Copyright 1995-2001,2006,2008-2009,2011-2012,2017-2018
+/* Copyright 1995-2001,2006,2008-2009,2011-2012,2017-2019
      Free Software Foundation, Inc.
 
    This file is part of Guile.
@@ -86,8 +86,10 @@ enum scm_fport_option
        that case.  */
     SCM_FPORT_OPTION_NOT_SEEKABLE = 1U<<1
   };
+SCM_INTERNAL int scm_i_fdes_is_valid (int fdes, long mode_bits);
 SCM_INTERNAL SCM scm_i_fdes_to_port (int fdes, long mode_bits, SCM name,
                                      unsigned options);
+
 #endif /* BUILDING_LIBGUILE */
 
 #endif  /* SCM_FPORTS_H */
index 504c28831b5c397fdc3d3de2118491d09ba040b1..2a9f963ac9fcce7fe91155ae9460e1573067e642 100644 (file)
 \f
 /* initializing standard and current I/O ports */
 
-typedef struct
-{
-  int fdes;
-  char *mode;
-} stream_body_data;
-
-/* proc to be called in scope of exception handler stream_handler. */
-static SCM
-stream_body (void *data)
-{
-  stream_body_data *body_data = (stream_body_data *) data;
-  SCM port = scm_fdes_to_port (body_data->fdes, body_data->mode, SCM_BOOL_F);
-  scm_set_port_revealed_x (port, SCM_INUM1);
-  return port;
-}
-
-/* exception handler for stream_body.  */
-static SCM
-stream_handler (void *data SCM_UNUSED,
-               SCM tag SCM_UNUSED,
-               SCM throw_args SCM_UNUSED)
-{
-  return SCM_BOOL_F;
-}
-
 /* Convert a file descriptor to a port, using scm_fdes_to_port.
    - set the revealed count for FILE's file descriptor to 1, so
-   that fdes won't be closed when the port object is GC'd.
-   - catch exceptions: allow Guile to be able to start up even
-   if it has been handed bogus stdin/stdout/stderr.  replace the
-   bad ports with void ports.  */
+   that fdes won't be closed when the port object is GC'd.  */
 static SCM
 scm_standard_stream_to_port (int fdes, char *mode)
 {
-  SCM port;
-  stream_body_data body_data;
-
-  body_data.fdes = fdes;
-  body_data.mode = mode;
-  port = scm_internal_catch (SCM_BOOL_T, stream_body, &body_data, 
-                            stream_handler, NULL);
-  if (scm_is_false (port))
-    port = scm_void_port (mode);
+  long mode_bits = scm_mode_bits (mode);
+
+  if (!scm_i_fdes_is_valid (fdes, mode_bits))
+    return scm_void_port (mode);
+
+  SCM port = scm_i_fdes_to_port (fdes, mode_bits, SCM_BOOL_F, 0);
+  scm_set_port_revealed_x (port, SCM_INUM1);
   return port;
 }