https://www.redhat.com/archives/libguestfs/2019-August/thread.html#00347 Thanks: Eric Blake and Daniel P. Berrangé --- common/utils/utils.h | 1 + server/connections.c | 4 ++-- server/crypto.c | 5 +++-- server/main.c | 23 +++++++++++++++++++++++ common/utils/utils.c | 29 +++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 4 deletions(-) diff --git a/common/utils/utils.h b/common/utils/utils.h index ebd5f66..a77d2cd 100644 --- a/common/utils/utils.h +++ b/common/utils/utils.h @@ -38,5 +38,6 @@ extern void uri_quote (const char *str, FILE *fp); extern int exit_status_to_nbd_error (int status, const char *cmd); extern int set_cloexec (int fd); extern int set_nonblock (int fd); +extern void close_or_nullify_fd (int fd); #endif /* NBDKIT_UTILS_H */ diff --git a/server/connections.c b/server/connections.c index c173df8..f57ab3e 100644 --- a/server/connections.c +++ b/server/connections.c @@ -489,7 +489,7 @@ static void raw_close (struct connection *conn) { if (conn->sockin >= 0) - close (conn->sockin); + close_or_nullify_fd (conn->sockin); if (conn->sockout >= 0 && conn->sockin != conn->sockout) - close (conn->sockout); + close_or_nullify_fd (conn->sockout); } diff --git a/server/crypto.c b/server/crypto.c index 9cd1bb0..6f97f2c 100644 --- a/server/crypto.c +++ b/server/crypto.c @@ -47,6 +47,7 @@ #include <assert.h> #include "internal.h" +#include "utils.h" #ifdef HAVE_GNUTLS @@ -404,9 +405,9 @@ crypto_close (struct connection *conn) gnutls_bye (session, GNUTLS_SHUT_RDWR); if (sockin >= 0) - close (sockin); + close_or_nullify_fd (sockin); if (sockout >= 0 && sockin != sockout) - close (sockout); + close_or_nullify_fd (sockout); gnutls_deinit (session); conn->crypto_session = NULL; diff --git a/server/main.c b/server/main.c index 65025a6..65bef30 100644 --- a/server/main.c +++ b/server/main.c @@ -60,6 +60,7 @@ static struct backend *open_filter_so (struct backend *next, size_t i, const cha static void start_serving (void); static void write_pidfile (void); static bool is_config_key (const char *key, size_t len); +static void open_std_file_descriptors (void); struct debug_flag *debug_flags; /* -D */ bool exit_with_parent; /* --exit-with-parent */ @@ -149,6 +150,13 @@ main (int argc, char *argv[]) size_t i; const char *magic_config_key; + /* Ensures that fds 0, 1 and 2 are open (on /dev/null if nothing + * else). This is so that nbdkit and plugins can assume these file + * descriptors are always open, which makes certain code easier to + * write. + */ + open_std_file_descriptors (); + threadlocal_init (); /* The default setting for TLS depends on whether we were @@ -930,3 +938,18 @@ is_config_key (const char *key, size_t len) return true; } + +static void +open_std_file_descriptors (void) +{ + int fds[] = { STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO }; + int i, fd, fl; + + for (i = 0; i < sizeof fds / sizeof fds[0]; ++i) { + fd = fds[i]; + fl = fcntl (fd, F_GETFL, NULL); + if (fl == -1 && errno == EBADF) + /* This is best effort - don't fail. */ + open ("/dev/null", fd == STDIN_FILENO ? O_RDONLY : O_WRONLY); + } +} diff --git a/common/utils/utils.c b/common/utils/utils.c index 32bc96a..c818d4c 100644 --- a/common/utils/utils.c +++ b/common/utils/utils.c @@ -190,3 +190,32 @@ set_nonblock (int fd) { } return fd; } + +/* Calls close (fd). As a special case if fd is stdin/stdout/stderr + * then it reopens the file descriptor on /dev/null. This allows us + * to maintain certain invariants for nbdkit and plugins. In all + * cases this ignores errors. + */ +void +close_or_nullify_fd (int fd) +{ + int flags; + int nfd; + + close (fd); + + if (fd == STDIN_FILENO) + flags = O_RDONLY; + else if (fd == STDOUT_FILENO || fd == STDERR_FILENO) + flags = O_WRONLY; + else + return; + + nfd = open ("/dev/null", flags); + if (nfd == -1) + return; + if (nfd != fd) { + dup2 (nfd, fd); + close (nfd); + } +} -- 2.22.0