tuwunel_service/admin/
execute.rs1use tokio::task::yield_now;
2use tuwunel_core::{Err, Result, debug, debug_info, error, implement, info};
3#[cfg(feature = "console")]
4use tuwunel_core::{log::is_terminal_mode, warn};
5
6use super::CommandOutput;
7
8pub(super) const SIGNAL: &str = "SIGUSR2";
9
10#[implement(super::Service)]
13#[cfg_attr(not(feature = "console"), expect(clippy::unused_async))]
14pub(super) async fn console_auto_start(&self) {
15 #[cfg(feature = "console")]
16 if self
17 .services
18 .server
19 .config
20 .admin_console_automatic
21 {
22 if !is_terminal_mode() {
23 warn!("Not starting the admin console: standard input is not a terminal");
24 return;
25 }
26
27 yield_now().await;
29 self.console.start();
30 }
31}
32
33#[implement(super::Service)]
35#[cfg_attr(not(feature = "console"), expect(clippy::unused_async))]
36pub(super) async fn console_auto_stop(&self) {
37 #[cfg(feature = "console")]
38 self.console.close().await;
39}
40
41#[implement(super::Service)]
43pub async fn startup_execute(&self) -> Result {
44 let commands = &self.services.server.config.admin_execute;
46
47 let smoketest = self.services.server.config.test.contains("smoke");
49
50 let errors = !smoketest
52 && self
53 .services
54 .server
55 .config
56 .admin_execute_errors_ignore;
57
58 for (i, command) in commands.iter().enumerate() {
59 if let Err(e) = self.execute_command(i, command.clone()).await
60 && !errors
61 {
62 return Err(e);
63 }
64
65 yield_now().await;
66 }
67
68 if smoketest {
71 debug_info!("Smoketest mode. All commands complete. Shutting down now...");
72 self.services
73 .server
74 .shutdown()
75 .inspect_err(error::inspect_log)
76 .expect("Error shutting down from smoketest");
77 }
78
79 Ok(())
80}
81
82#[implement(super::Service)]
84pub(super) async fn signal_execute(&self) -> Result {
85 let commands = self
87 .services
88 .server
89 .config
90 .admin_signal_execute
91 .clone();
92
93 let ignore_errors = self
95 .services
96 .server
97 .config
98 .admin_execute_errors_ignore;
99
100 for (i, command) in commands.iter().enumerate() {
101 if let Err(e) = self.execute_command(i, command.clone()).await
102 && !ignore_errors
103 {
104 return Err(e);
105 }
106
107 yield_now().await;
108 }
109
110 Ok(())
111}
112
113#[implement(super::Service)]
115async fn execute_command(&self, i: usize, command: String) -> Result {
116 debug!("Execute command #{i}: executing {command:?}");
117
118 match self.command_in_place(command, None).await {
119 | Ok(Some(output)) => Self::execute_command_output(i, &output),
120 | Err(output) => Self::execute_command_error(i, &output),
121 | Ok(None) => {
122 info!("Execute command #{i} completed (no output).");
123 Ok(())
124 },
125 }
126}
127
128#[cfg(feature = "console")]
129#[implement(super::Service)]
130fn execute_command_output(i: usize, content: &CommandOutput) -> Result {
131 debug_info!("Execute command #{i} completed:");
132 super::console::print(content.as_str());
133 Ok(())
134}
135
136#[cfg(feature = "console")]
137#[implement(super::Service)]
138fn execute_command_error(i: usize, content: &CommandOutput) -> Result {
139 super::console::print_err(content.as_str());
140 Err!(debug_error!("Execute command #{i} failed."))
141}
142
143#[cfg(not(feature = "console"))]
144#[implement(super::Service)]
145fn execute_command_output(i: usize, content: &CommandOutput) -> Result {
146 info!("Execute command #{i} completed:\n{:#}", content.as_str());
147 Ok(())
148}
149
150#[cfg(not(feature = "console"))]
151#[implement(super::Service)]
152fn execute_command_error(i: usize, content: &CommandOutput) -> Result {
153 Err!(error!("Execute command #{i} failed:\n{:#}", content.as_str()))
154}