From: Simon McVittie Date: Thu, 21 Jul 2016 07:23:12 +0000 (+0100) Subject: _dbus_ensure_standard_fds: new function to ensure std* fds are open X-Git-Tag: dbus-1.12.0~445 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=69123a6bd2adabbaec1f770fc4573fc3ed4ceca6;p=platform%2Fupstream%2Fdbus.git _dbus_ensure_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. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=97008 Signed-off-by: Simon McVittie Reviewed-by: Thiago Macieira --- diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index ce8cbf7..700b831 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -134,6 +134,81 @@ #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. Callers are expected to combine *error_str_p + * with _dbus_strerror (errno) to get a full error report. + * + * This function can only be called while single-threaded: either during + * startup of an executable, or after fork(). + */ +dbus_bool_t +_dbus_ensure_standard_fds (DBusEnsureStandardFdsFlags flags, + const char **error_str_p) +{ + static int const relevant_flag[] = { DBUS_FORCE_STDIN_NULL, + DBUS_FORCE_STDOUT_NULL, + DBUS_FORCE_STDERR_NULL }; + /* Should always get replaced with the real error before use */ + 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, so the only way this assertion + * could fail is if another thread closed one, and we document + * this function as not safe for multi-threading. */ + _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..279cae2 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_FORCE_STDIN_NULL = (1 << 0), + DBUS_FORCE_STDOUT_NULL = (1 << 1), + DBUS_FORCE_STDERR_NULL = (1 << 2) +} DBusEnsureStandardFdsFlags; + +DBUS_PRIVATE_EXPORT +dbus_bool_t _dbus_ensure_standard_fds (DBusEnsureStandardFdsFlags flags, + const char **error_str_p); + /** @} */ DBUS_END_DECLS