holochain_cli_run_local_services/
lib.rs1use clap::Parser;
2use std::io::{Error, Result};
3use std::sync::Arc;
4use tokio::io::AsyncWriteExt;
5
6#[derive(Debug, Parser)]
8#[command(version, about)]
9pub struct HcRunLocalServices {
10 #[arg(long)]
13 bootstrap_address_path: Option<std::path::PathBuf>,
14
15 #[arg(long, default_value = "127.0.0.1")]
17 bootstrap_interface: String,
18
19 #[arg(short, long, default_value = "0")]
22 bootstrap_port: u16,
23
24 #[arg(long)]
26 disable_bootstrap: bool,
27
28 #[arg(long)]
31 signal_address_path: Option<std::path::PathBuf>,
32
33 #[arg(long, default_value = "127.0.0.1, [::1]")]
35 signal_interfaces: String,
36
37 #[arg(short, long, default_value = "0")]
40 signal_port: u16,
41
42 #[arg(long)]
44 disable_signal: bool,
45}
46
47struct AOut(Option<tokio::fs::File>);
48
49impl AOut {
50 pub async fn new(p: &Option<std::path::PathBuf>) -> Result<Self> {
51 Ok(Self(if let Some(path) = p {
52 Some(
53 tokio::fs::OpenOptions::new()
54 .write(true)
55 .create_new(true)
56 .open(path)
57 .await?,
58 )
59 } else {
60 None
61 }))
62 }
63
64 pub async fn write(&mut self, s: String) -> Result<()> {
65 if let Some(f) = &mut self.0 {
66 f.write_all(s.as_bytes()).await?;
67 }
68 Ok(())
69 }
70
71 pub async fn close(mut self) -> Result<()> {
72 if let Some(f) = &mut self.0 {
73 f.flush().await?;
74 f.shutdown().await?;
75 }
76 Ok(())
77 }
78}
79
80impl HcRunLocalServices {
81 #[allow(clippy::too_many_arguments)]
82 pub fn new(
83 bootstrap_address_path: Option<std::path::PathBuf>,
84 bootstrap_interface: String,
85 bootstrap_port: u16,
86 disable_bootstrap: bool,
87 signal_address_path: Option<std::path::PathBuf>,
88 signal_interfaces: String,
89 signal_port: u16,
90 disable_signal: bool,
91 ) -> Self {
92 Self {
93 bootstrap_address_path,
94 bootstrap_interface,
95 bootstrap_port,
96 disable_bootstrap,
97 signal_address_path,
98 signal_interfaces,
99 signal_port,
100 disable_signal,
101 }
102 }
103
104 pub async fn run(self) {
105 if let Err(err) = self.run_err().await {
106 eprintln!("run-local-services error");
107 eprintln!("{err:#?}");
108 }
109 }
110
111 pub async fn run_err(self) -> Result<()> {
112 let mut task_list = Vec::new();
113
114 if !self.disable_bootstrap {
115 let bs_ip: std::net::IpAddr = self.bootstrap_interface.parse().map_err(Error::other)?;
116 let bs_addr = std::net::SocketAddr::from((bs_ip, self.bootstrap_port));
117 let (bs_driver, bs_addr, shutdown) = kitsune_p2p_bootstrap::run(bs_addr, vec![])
118 .await
119 .map_err(Error::other)?;
120 std::mem::forget(shutdown);
121 task_list.push(bs_driver);
122
123 let mut a_out = AOut::new(&self.bootstrap_address_path).await?;
124
125 for addr in tx_addr(bs_addr)? {
126 a_out.write(format!("http://{addr}\n")).await?;
127 println!("# HC BOOTSTRAP - ADDR: http://{addr}");
128 }
129
130 a_out.close().await?;
131
132 println!("# HC BOOTSTRAP - RUNNING");
133 }
134
135 if !self.disable_signal {
136 let bind = self
137 .signal_interfaces
138 .split(',')
139 .map(|i| format!("{}:{}", i.trim(), self.signal_port))
140 .collect();
141 println!("BIND: {bind:?}");
142 let config = sbd_server::Config {
143 bind,
144 ..Default::default()
145 };
146 tracing::info!(?config);
147
148 let sig_hnd = sbd_server::SbdServer::new(Arc::new(config)).await?;
149
150 let addr_list = sig_hnd.bind_addrs().to_vec();
151
152 task_list.push(Box::pin(async move {
154 let _sig_hnd = sig_hnd;
155 std::future::pending().await
156 }));
157
158 let mut a_out = AOut::new(&self.signal_address_path).await?;
159
160 for addr in addr_list {
161 a_out.write(format!("ws://{addr}\n")).await?;
162 println!("# HC SIGNAL - ADDR: ws://{addr}");
163 }
164
165 a_out.close().await?;
166
167 println!("# HC SIGNAL - RUNNING");
168 }
169
170 if task_list.is_empty() {
171 println!("All Services Disabled - Aborting");
172 return Ok(());
173 }
174
175 futures::future::join_all(task_list).await;
176
177 Ok(())
178 }
179}
180
181fn tx_addr(addr: std::net::SocketAddr) -> Result<Vec<std::net::SocketAddr>> {
182 if addr.ip().is_unspecified() {
183 let port = addr.port();
184 let mut list = Vec::new();
185 let include_v6 = addr.ip().is_ipv6();
186
187 for iface in if_addrs::get_if_addrs()? {
188 if iface.ip().is_ipv6() && !include_v6 {
189 continue;
190 }
191 list.push((iface.ip(), port).into());
192 }
193
194 Ok(list)
195 } else {
196 Ok(vec![addr])
197 }
198}