1use std::{
2 io::{Read, Write},
3 net::TcpStream,
4 process::Command,
5 sync::Arc,
6 time::Duration,
7};
8
9#[cfg(not(target_family = "windows"))]
10use std::os::unix::net::UnixStream;
11
12use crate::{help::Help, sys::log::Log};
13
14use super::{
15 action::ActMap,
16 go::Go,
17 init::{Addr, Init, Mode},
18};
19
20#[derive(Debug)]
26pub(crate) struct App {
27 pub init: Init,
29}
30
31impl App {
32 pub fn new(name: &str, version: &str, desc: &str, allow_no_config: bool) -> Option<App> {
34 let init = Init::new(name, version, desc, allow_no_config)?;
35 Some(App { init })
36 }
37
38 pub fn run(&self, func: &impl Fn() -> ActMap) {
40 Log::info(17, Some(format!("{:?}", self.init.mode)));
41 match self.init.mode {
42 Mode::Start => self.start(),
43 Mode::Stop => App::stop(Arc::clone(&self.init.conf.rpc), self.init.conf.stop_signal),
44 Mode::Help => Help::show(&self.init.conf.name, &self.init.conf.version, &self.init.conf.desc),
45 Mode::Go => Go::run(&self.init, func),
46 Mode::Status => self.status(),
47 };
48 }
49
50 fn status(&self) {
52 let mut buf: [u8; 8] = [0; 8];
53 let mut status = Vec::with_capacity(1024);
54 #[allow(clippy::infallible_destructuring_match)]
55 match self.init.conf.rpc.as_ref() {
56 Addr::SocketAddr(socket) => {
57 let mut tcp = match TcpStream::connect_timeout(socket, Duration::from_secs(2)) {
58 Ok(t) => t,
59 Err(e) => {
60 Log::stop(213, Some(e.to_string()));
61 return;
62 }
63 };
64 if let Err(e) = tcp.write(&self.init.conf.status_signal.to_be_bytes()) {
66 Log::stop(224, Some(e.to_string()));
67 return;
68 };
69 if let Err(e) = tcp.set_read_timeout(Some(Duration::from_secs(30))) {
70 Log::stop(216, Some(e.to_string()));
71 return;
72 };
73
74 if let Err(e) = tcp.read_exact(&mut buf) {
76 Log::stop(217, Some(e.to_string()));
77 return;
78 };
79 if let Err(e) = tcp.read_to_end(&mut status) {
80 Log::stop(225, Some(e.to_string()));
81 return;
82 };
83 }
84 #[cfg(not(target_family = "windows"))]
85 Addr::Uds(path) => {
86 let mut tcp = match UnixStream::connect(path) {
87 Ok(t) => t,
88 Err(e) => {
89 Log::stop(213, Some(e.to_string()));
90 return;
91 }
92 };
93 if let Err(e) = tcp.set_write_timeout(Some(Duration::new(2, 0))) {
94 Log::stop(223, Some(e.to_string()));
95 return;
96 };
97 if let Err(e) = tcp.write(&self.init.conf.status_signal.to_be_bytes()) {
98 Log::stop(224, Some(e.to_string()));
99 return;
100 };
101 if let Err(e) = tcp.set_read_timeout(Some(Duration::from_secs(30))) {
102 Log::stop(216, Some(e.to_string()));
103 return;
104 };
105
106 if let Err(e) = tcp.read_exact(&mut buf) {
108 Log::stop(217, Some(e.to_string()));
109 return;
110 };
111 if let Err(e) = tcp.read_to_end(&mut status) {
112 Log::stop(225, Some(e.to_string()));
113 return;
114 };
115 }
116 };
117 let pid = u64::from_be_bytes(buf);
118 let answ = match String::from_utf8(status) {
119 Ok(a) => a,
120 Err(e) => {
121 Log::stop(226, Some(e.to_string()));
122 return;
123 }
124 };
125 println!("Answer PID={}\n{}", pid, answ);
126 }
127
128 pub(crate) fn stop(rpc: Arc<Addr>, stop: i64) {
130 let mut buf: [u8; 8] = [0; 8];
131 #[allow(clippy::infallible_destructuring_match)]
132 match rpc.as_ref() {
133 Addr::SocketAddr(socket) => {
134 let mut tcp = match TcpStream::connect_timeout(socket, Duration::from_secs(2)) {
135 Ok(t) => t,
136 Err(e) => {
137 Log::stop(213, Some(e.to_string()));
138 return;
139 }
140 };
141 if let Err(e) = tcp.write(&stop.to_be_bytes()) {
143 Log::stop(214, Some(e.to_string()));
144 return;
145 };
146 if let Err(e) = tcp.set_read_timeout(Some(Duration::from_secs(30))) {
147 Log::stop(216, Some(e.to_string()));
148 return;
149 };
150
151 if let Err(e) = tcp.read_exact(&mut buf) {
153 Log::stop(217, Some(e.to_string()));
154 return;
155 };
156 }
157 #[cfg(not(target_family = "windows"))]
158 Addr::Uds(path) => {
159 let mut tcp = match UnixStream::connect(path) {
160 Ok(t) => t,
161 Err(e) => {
162 Log::stop(213, Some(e.to_string()));
163 return;
164 }
165 };
166 if let Err(e) = tcp.set_write_timeout(Some(Duration::new(2, 0))) {
167 Log::stop(223, Some(e.to_string()));
168 return;
169 };
170 if let Err(e) = tcp.write(&stop.to_be_bytes()) {
171 Log::stop(214, Some(e.to_string()));
172 return;
173 };
174 if let Err(e) = tcp.set_read_timeout(Some(Duration::from_secs(30))) {
175 Log::stop(216, Some(e.to_string()));
176 return;
177 };
178
179 if let Err(e) = tcp.read_exact(&mut buf) {
181 Log::stop(217, Some(e.to_string()));
182 return;
183 };
184 }
185 };
186
187 let pid = u64::from_be_bytes(buf);
188 Log::info(218, Some(format!("Answer PID={}", pid)));
189 }
190
191 #[cfg(target_family = "windows")]
193 fn start(&self) {
194 let path = App::to_win_path(&self.init.exe_path);
195 let exe = App::to_win_path(&self.init.exe_file);
196 const DETACHED_PROCESS: u32 = 0x00000008;
197 const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
198 const CREATE_NO_WINDOW: u32 = 0x08000000;
199
200 let args = ["go", "-r", &self.init.root_path];
201 use std::os::windows::process::CommandExt;
202
203 match Command::new(&exe)
204 .args(args)
205 .current_dir(&path)
206 .creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW)
207 .spawn()
208 {
209 Ok(c) => Log::info(211, Some(format!("{} {}. PID: {}", &exe, args.join(" "), c.id()))),
210 Err(e) => Log::stop(212, Some(format!("{} {}. Error: {}", &exe, args.join(" "), e))),
211 };
212 }
213
214 #[cfg(not(target_family = "windows"))]
216 fn start(&self) {
217 let path = &self.init.exe_path;
218 let exe = &self.init.exe_file;
219
220 let args = ["go", "-r", &self.init.root_path];
221 match Command::new(exe).args(&args[..]).current_dir(path.as_ref()).spawn() {
222 Ok(c) => Log::info(211, Some(format!("{} {}. PID: {}", &exe, args.join(" "), c.id()))),
223 Err(e) => Log::stop(212, Some(format!("{} {}. Error: {}", &exe, args.join(" "), e))),
224 };
225 }
226
227 #[cfg(target_family = "windows")]
229 fn to_win_path(text: &str) -> String {
230 text.replace('/', r"\")
231 }
232}