1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-04-29 19:30:36 +02:00

Avoid throwing exceptions during early boot if stdin is closed

* 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.
This commit is contained in:
Andy Wingo 2019-12-13 13:52:58 +01:00
parent cf693d862d
commit dbc93d6195
3 changed files with 37 additions and 55 deletions

View file

@ -401,6 +401,29 @@ SCM_DEFINE (scm_i_open_file, "open-file", 2, 0, 1,
/* Building Guile ports from a file descriptor. */ /* 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'. /* Build a Scheme port from an open file descriptor `fdes'.
MODE indicates whether FILE is open for reading or writing; it uses MODE indicates whether FILE is open for reading or writing; it uses
the same notation as open-file's second argument. 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) if (options & SCM_FPORT_OPTION_VERIFY)
{ {
/* Check that the foreign FD is valid and matches the mode errno = 0;
bits. */ if (!scm_i_fdes_is_valid (fdes, 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))))
{ {
if (errno)
SCM_SYSERROR;
SCM_MISC_ERROR ("requested file mode not available on fdes", SCM_MISC_ERROR ("requested file mode not available on fdes",
SCM_EOL); 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), fp = (scm_t_fport *) scm_gc_malloc_pointerless (sizeof (scm_t_fport),

View file

@ -1,7 +1,7 @@
#ifndef SCM_FPORTS_H #ifndef SCM_FPORTS_H
#define 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. Free Software Foundation, Inc.
This file is part of Guile. This file is part of Guile.
@ -86,8 +86,10 @@ enum scm_fport_option
that case. */ that case. */
SCM_FPORT_OPTION_NOT_SEEKABLE = 1U<<1 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, SCM_INTERNAL SCM scm_i_fdes_to_port (int fdes, long mode_bits, SCM name,
unsigned options); unsigned options);
#endif /* BUILDING_LIBGUILE */ #endif /* BUILDING_LIBGUILE */
#endif /* SCM_FPORTS_H */ #endif /* SCM_FPORTS_H */

View file

@ -156,49 +156,19 @@
/* initializing standard and current I/O ports */ /* 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. /* Convert a file descriptor to a port, using scm_fdes_to_port.
- set the revealed count for FILE's file descriptor to 1, so - 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. 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. */
static SCM static SCM
scm_standard_stream_to_port (int fdes, char *mode) scm_standard_stream_to_port (int fdes, char *mode)
{ {
SCM port; long mode_bits = scm_mode_bits (mode);
stream_body_data body_data;
body_data.fdes = fdes; if (!scm_i_fdes_is_valid (fdes, mode_bits))
body_data.mode = mode; return scm_void_port (mode);
port = scm_internal_catch (SCM_BOOL_T, stream_body, &body_data,
stream_handler, NULL); SCM port = scm_i_fdes_to_port (fdes, mode_bits, SCM_BOOL_F, 0);
if (scm_is_false (port)) scm_set_port_revealed_x (port, SCM_INUM1);
port = scm_void_port (mode);
return port; return port;
} }