Skip to content

Commit fb40958

Browse files
authored
rust: support and default to --host localhost (#4804)
Summary: We now interpret `--host` as a string that can be `getaddrinfo`d rather than requiring it to be a literal IP address. Thus, we can support hostnames like `localhost` that [may have multiple resolutions][play]: ``` [src/main.rs:3] ("localhost", 6006).to_socket_addrs().unwrap().collect::<Vec<_>>() = [ 127.0.0.1:6006, [::1]:6006, ] ``` [play]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=15504b49f87e983b4748ca976dcb3bd1 Thus, we change the default `--host` from `::1` to `localhost` to support systems where `::1` may not be available. Should fix #4801. We also improve the error message on bind failure: it’s pretty-printed instead of `Debug`-formatted, and it includes the problematic address. Test Plan: From `tensorboard/data/server/`, run `cargo build`, then reproduce the failure in Docker: ``` $ img=tensorflow/tensorflow@sha256:31775f136e5fe2d50ae075100dff335e9ae0954b8f9b529791a8bf739f3dd415 $ v="$PWD/target/debug:/rustboard-bin:ro" $ docker run --rm -it -v "$v" "$img" /rustboard-bin/rustboard --logdir /tmp --host ::1 fatal: failed to bind to ("::1", 6806): Cannot assign requested address (os error 99) ``` …then try again with default `--host` and note that it works: ``` $ docker run --rm -it -v "$v" "$img" /rustboard-bin/rustboard --logdir /tmp listening on 127.0.0.1:6806 ``` (You may need to `docker kill $(docker ps -q)` the thing afterward.) wchargin-branch: rust-host-getaddrinfo
1 parent 78569c0 commit fb40958

File tree

1 file changed

+17
-11
lines changed

1 file changed

+17
-11
lines changed

tensorboard/data/server/cli.rs

+17-11
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use clap::Clap;
1919
use log::{debug, error, info, LevelFilter};
2020
use std::fs::File;
2121
use std::io::{Read, Write};
22-
use std::net::{IpAddr, SocketAddr};
2322
use std::path::{Path, PathBuf};
2423
use std::str::FromStr;
2524
use std::sync::Arc;
@@ -52,12 +51,12 @@ struct Opts {
5251
#[clap(long, setting(clap::ArgSettings::AllowEmptyValues))]
5352
logdir: PathBuf,
5453

55-
/// Bind to this IP address
54+
/// Bind to this host name
5655
///
57-
/// IP address to bind this server to. May be an IPv4 address (e.g., 127.0.0.1 or 0.0.0.0) or
58-
/// an IPv6 address (e.g., ::1 or ::0).
59-
#[clap(long, default_value = "::1")]
60-
host: IpAddr,
56+
/// Host to bind this server to. May be an IPv4 address (e.g., 127.0.0.1 or 0.0.0.0), an IPv6
57+
/// address (e.g., ::1 or ::0), or a string like `localhost` to pass to `getaddrinfo(3)`.
58+
#[clap(long, default_value = "localhost")]
59+
host: String,
6160

6261
/// Bind to this port
6362
///
@@ -160,6 +159,7 @@ impl FromStr for ReloadStrategy {
160159
/// Exit code for failure of [`DynLogdir::new`].
161160
// Keep in sync with docs on `Opts::logdir`.
162161
const EXIT_BAD_LOGDIR: i32 = 8;
162+
const EXIT_FAILED_TO_BIND: i32 = 9;
163163

164164
#[tokio::main]
165165
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -172,12 +172,14 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
172172
debug!("Parsed options: {:?}", opts);
173173
let data_location = opts.logdir.display().to_string();
174174

175+
let error_file_path = opts.error_file.as_ref().map(PathBuf::as_ref);
176+
175177
// Create the logdir outside an async runtime (see docs for `DynLogdir::new`).
176-
let (raw_logdir, error_file) = (opts.logdir, opts.error_file);
178+
let raw_logdir = opts.logdir;
177179
let logdir = tokio::task::spawn_blocking(|| DynLogdir::new(raw_logdir))
178180
.await?
179181
.unwrap_or_else(|e| {
180-
write_startup_error(error_file.as_ref().map(|p| p.as_ref()), e);
182+
write_startup_error(error_file_path, &e.to_string());
181183
std::process::exit(EXIT_BAD_LOGDIR);
182184
});
183185

@@ -188,8 +190,12 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
188190
.expect("failed to spawn stdin watcher thread");
189191
}
190192

191-
let addr = SocketAddr::new(opts.host, opts.port);
192-
let listener = TcpListener::bind(addr).await?;
193+
let addr = (opts.host.as_str(), opts.port);
194+
let listener = TcpListener::bind(addr).await.unwrap_or_else(|e| {
195+
let msg = format!("failed to bind to {:?}: {}", addr, e);
196+
write_startup_error(error_file_path, &msg);
197+
std::process::exit(EXIT_FAILED_TO_BIND);
198+
});
193199
let bound = listener.local_addr()?;
194200

195201
if let Some(port_file) = opts.port_file {
@@ -273,7 +279,7 @@ fn write_port_file(path: &Path, port: u16) -> std::io::Result<()> {
273279
}
274280

275281
/// Writes error to the given file, or to stderr as a fallback.
276-
fn write_startup_error(path: Option<&Path>, error: dynamic_logdir::Error) {
282+
fn write_startup_error(path: Option<&Path>, error: &str) {
277283
let write_to_file = |path: &Path| -> std::io::Result<()> {
278284
let mut f = File::create(path)?;
279285
writeln!(f, "{}", error)?;

0 commit comments

Comments
 (0)