Skip to main content

tuwunel_service/admin/
execute.rs

1use ruma::events::room::message::RoomMessageEventContent;
2use tokio::task::yield_now;
3use tuwunel_core::{Err, Result, debug, debug_info, error, implement, info};
4
5pub(super) const SIGNAL: &str = "SIGUSR2";
6
7/// Possibly spawn the terminal console at startup if configured.
8#[implement(super::Service)]
9#[cfg_attr(not(feature = "console"), expect(clippy::unused_async))]
10pub(super) async fn console_auto_start(&self) {
11	#[cfg(feature = "console")]
12	if self
13		.services
14		.server
15		.config
16		.admin_console_automatic
17	{
18		// Allow more of the startup sequence to execute before spawning
19		yield_now().await;
20		self.console.start();
21	}
22}
23
24/// Shutdown the console when the admin worker terminates.
25#[implement(super::Service)]
26#[cfg_attr(not(feature = "console"), expect(clippy::unused_async))]
27pub(super) async fn console_auto_stop(&self) {
28	#[cfg(feature = "console")]
29	self.console.close().await;
30}
31
32/// Execute admin commands after startup
33#[implement(super::Service)]
34pub async fn startup_execute(&self) -> Result {
35	// List of commands to execute
36	let commands = &self.services.server.config.admin_execute;
37
38	// Determine if we're running in smoketest-mode which will change some behaviors
39	let smoketest = self.services.server.config.test.contains("smoke");
40
41	// When true, errors are ignored and startup continues.
42	let errors = !smoketest
43		&& self
44			.services
45			.server
46			.config
47			.admin_execute_errors_ignore;
48
49	for (i, command) in commands.iter().enumerate() {
50		if let Err(e) = self.execute_command(i, command.clone()).await
51			&& !errors
52		{
53			return Err(e);
54		}
55
56		yield_now().await;
57	}
58
59	// The smoketest functionality is placed here for now and simply initiates
60	// shutdown after all commands have executed.
61	if smoketest {
62		debug_info!("Smoketest mode. All commands complete. Shutting down now...");
63		self.services
64			.server
65			.shutdown()
66			.inspect_err(error::inspect_log)
67			.expect("Error shutting down from smoketest");
68	}
69
70	Ok(())
71}
72
73/// Execute admin commands after signal
74#[implement(super::Service)]
75pub(super) async fn signal_execute(&self) -> Result {
76	// List of commands to execute
77	let commands = self
78		.services
79		.server
80		.config
81		.admin_signal_execute
82		.clone();
83
84	// When true, errors are ignored and execution continues.
85	let ignore_errors = self
86		.services
87		.server
88		.config
89		.admin_execute_errors_ignore;
90
91	for (i, command) in commands.iter().enumerate() {
92		if let Err(e) = self.execute_command(i, command.clone()).await
93			&& !ignore_errors
94		{
95			return Err(e);
96		}
97
98		yield_now().await;
99	}
100
101	Ok(())
102}
103
104/// Execute one admin command after startup or signal
105#[implement(super::Service)]
106async fn execute_command(&self, i: usize, command: String) -> Result {
107	debug!("Execute command #{i}: executing {command:?}");
108
109	match self.command_in_place(command, None).await {
110		| Ok(Some(output)) => Self::execute_command_output(i, &output),
111		| Err(output) => Self::execute_command_error(i, &output),
112		| Ok(None) => {
113			info!("Execute command #{i} completed (no output).");
114			Ok(())
115		},
116	}
117}
118
119#[cfg(feature = "console")]
120#[implement(super::Service)]
121fn execute_command_output(i: usize, content: &RoomMessageEventContent) -> Result {
122	debug_info!("Execute command #{i} completed:");
123	super::console::print(content.body());
124	Ok(())
125}
126
127#[cfg(feature = "console")]
128#[implement(super::Service)]
129fn execute_command_error(i: usize, content: &RoomMessageEventContent) -> Result {
130	super::console::print_err(content.body());
131	Err!(debug_error!("Execute command #{i} failed."))
132}
133
134#[cfg(not(feature = "console"))]
135#[implement(super::Service)]
136fn execute_command_output(i: usize, content: &RoomMessageEventContent) -> Result {
137	info!("Execute command #{i} completed:\n{:#}", content.body());
138	Ok(())
139}
140
141#[cfg(not(feature = "console"))]
142#[implement(super::Service)]
143fn execute_command_error(i: usize, content: &RoomMessageEventContent) -> Result {
144	Err!(error!("Execute command #{i} failed:\n{:#}", content.body()))
145}