From aa0e29f9fd000a0d341f00f050c5c15f0a8aa524 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 20 Jul 2016 09:49:34 +0100 Subject: [PATCH 03/10] _dbus_open_standard_fds: new function to ensure std* fds are open This function opens stdin, stdout, stderr pointing to /dev/null if they aren't already open. Optionally, it can also replace whatever is available on those fds with /dev/null. To allow for use in contexts where only async-signal-safe functions should be used, such as between fork() and a following exec(), this function does not use conventional libdbus error handling (which would require malloc). Instead, it sets errno and returns an explanatory string. Signed-off-by: Simon McVittie --- dbus/dbus-sysdeps-unix.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ dbus/dbus-sysdeps-unix.h | 11 ++++++++ 2 files changed, 82 insertions(+) diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index ce8cbf7..b00546e 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -134,6 +134,77 @@ #endif /* Solaris */ +/** + * Ensure that the standard file descriptors stdin, stdout and stderr + * are open, by opening /dev/null if necessary. + * + * This function does not use DBusError, to avoid calling malloc(), so + * that it can be used in contexts where an async-signal-safe function + * is required (for example after fork()). Instead, on failure it sets + * errno and returns something like "Failed to open /dev/null" in + * *error_str_p. + * + * This function can only be called while single-threaded: either during + * startup of an executable, or after fork(). + */ +dbus_bool_t +_dbus_open_standard_fds (DBusOpenStandardFdsFlags flags, + const char **error_str_p) +{ + static int const relevant_flag[] = { DBUS_OPEN_STDIN_NULL, + DBUS_OPEN_STDOUT_NULL, + DBUS_OPEN_STDERR_NULL }; + const char *error_str = "Failed mysteriously"; + int devnull = -1; + int saved_errno; + /* This function relies on the standard fds having their POSIX values. */ + _DBUS_STATIC_ASSERT (STDIN_FILENO == 0); + _DBUS_STATIC_ASSERT (STDOUT_FILENO == 1); + _DBUS_STATIC_ASSERT (STDERR_FILENO == 2); + int i; + + for (i = STDIN_FILENO; i <= STDERR_FILENO; i++) + { + /* Because we rely on being single-threaded, and we want the + * standard fds to not be close-on-exec, we don't set it + * close-on-exec. */ + if (devnull < i) + devnull = open ("/dev/null", O_RDWR); + + if (devnull < 0) + { + error_str = "Failed to open /dev/null"; + goto out; + } + + /* We already opened all fds < i. */ + _dbus_assert (devnull >= i); + + if (devnull != i && (flags & relevant_flag[i]) != 0) + { + if (dup2 (devnull, i) < 0) + { + error_str = "Failed to dup2 /dev/null onto a standard fd"; + goto out; + } + } + } + + error_str = NULL; + +out: + saved_errno = errno; + + if (devnull > STDERR_FILENO) + close (devnull); + + if (error_str_p != NULL) + *error_str_p = error_str; + + errno = saved_errno; + return (error_str == NULL); +} + static dbus_bool_t _dbus_set_fd_nonblocking (int fd, DBusError *error); diff --git a/dbus/dbus-sysdeps-unix.h b/dbus/dbus-sysdeps-unix.h index 72f3ecf..fd3598d 100644 --- a/dbus/dbus-sysdeps-unix.h +++ b/dbus/dbus-sysdeps-unix.h @@ -154,6 +154,17 @@ dbus_bool_t _dbus_append_address_from_socket (DBusSocket fd, DBUS_PRIVATE_EXPORT void _dbus_fd_set_close_on_exec (int fd); +typedef enum +{ + DBUS_OPEN_STDIN_NULL = (1 << 0), + DBUS_OPEN_STDOUT_NULL = (1 << 1), + DBUS_OPEN_STDERR_NULL = (1 << 2) +} DBusOpenStandardFdsFlags; + +DBUS_PRIVATE_EXPORT +dbus_bool_t _dbus_open_standard_fds (DBusOpenStandardFdsFlags flags, + const char **error_str_p); + /** @} */ DBUS_END_DECLS -- 2.8.1