1use super::process_lock::ProcessLock;
2use super::process_mutex::{ProcessMutex, CURRENT_PROC_LOCK, SERVICE_NAME};
3use clap::{App, Arg, ArgMatches};
4use std::error::Error;
5
6pub trait ProcessCmdFuncs: Send + Sync {
7 fn exit_process(&self, action: ProcessAction, code: i32) -> !;
8}
9
10struct DefaultProcessCmdFuncs;
11
12impl ProcessCmdFuncs for DefaultProcessCmdFuncs {
13 fn exit_process(&self, _action: ProcessAction, code: i32) -> ! {
14 std::process::exit(code)
15 }
16}
17
18static PROCESS_CMD_FUNCS: once_cell::sync::OnceCell<Box<dyn ProcessCmdFuncs>> =
19 once_cell::sync::OnceCell::new();
20
21pub fn get_process_cmd_funcs() -> &'static Box<dyn ProcessCmdFuncs> {
22 PROCESS_CMD_FUNCS.get_or_init(|| Box::new(DefaultProcessCmdFuncs {}))
23}
24
25pub fn set_process_cmd_funcs(
26 funcs: Box<dyn ProcessCmdFuncs>,
27) -> Result<(), Box<dyn ProcessCmdFuncs>> {
28 PROCESS_CMD_FUNCS.set(funcs)
29}
30
31#[derive(Debug, Eq, PartialEq, Clone, Copy)]
32pub enum ProcessAction {
33 Start,
34 Stop,
35 Status,
36 Install,
37}
38
39#[derive(Debug)]
40pub enum ProcessStatusCode {
41 NotExists = 0,
42 Running = 1,
43 RunningOther = 2,
44}
45
46impl ProcessStatusCode {
47 pub fn is_running_other(exit_code: i32) -> bool {
48 if exit_code == Self::RunningOther as i32 {
49 true
50 } else {
51 if exit_code == -1 || exit_code == 255 {
53 true
54 } else {
55 false
56 }
57 }
58 }
59}
60pub fn prepare_args<'a, 'b>(args: App<'a, 'b>) -> App<'a, 'b> {
61 args.arg(
62 Arg::with_name("start")
63 .long("start")
64 .takes_value(false)
65 .help("Start service"),
66 )
67 .arg(
68 Arg::with_name("stop")
69 .long("stop")
70 .takes_value(false)
71 .help("Stop service that if service still running"),
72 )
73 .arg(
74 Arg::with_name("status")
75 .long("status")
76 .takes_value(false)
77 .help("Check service is running or not"),
78 )
79 .arg(
80 Arg::with_name("install")
81 .long("install")
82 .takes_value(false)
83 .help("Install service when first install or update"),
84 )
85 .arg(
86 Arg::with_name("fid")
87 .long("fid")
88 .takes_value(true)
89 .help("Current service package fid when check status"),
90 )
91 .arg(
92 Arg::with_name("console-log")
93 .long("console-log")
94 .takes_value(false)
95 .help("Use console log instead"),
96 )
97 .arg(
98 Arg::with_name("cmd-log")
99 .long("cmd-log")
100 .takes_value(false)
101 .help("Output full cmd log to {service-name}-{cmd} file"),
102 )
103}
104
105pub fn parse_cmd(service_name: &str, matches: &ArgMatches) -> (ProcessAction, Option<String>) {
106 let mut fid: Option<String> = None;
107 let mut cmd = ProcessAction::Start;
108
109 if matches.is_present("status") {
110 cmd = ProcessAction::Status;
111
112 match matches.value_of("fid") {
113 Some(v) => fid = Some(v.to_string()),
114 None => {
115 warn!(
116 "fid param not specified with check status!!! service={}",
117 service_name
118 );
119 }
120 }
121 } else if matches.is_present("stop") {
122 cmd = ProcessAction::Stop;
123 } else if matches.is_present("install") {
124 cmd = ProcessAction::Install;
125 }
126
127 (cmd, fid)
128}
129
130fn check_current_fid(fid: &str) -> Result<bool, Box<dyn Error>> {
132 let fid = fid.to_owned();
133
134 let path = match std::env::current_exe() {
135 Ok(v) => v,
136 Err(e) => {
137 let msg = format!("get current_exe failed! err={}", e);
138 error!("{}", msg);
139
140 return Err(Box::<dyn Error>::from(msg));
141 }
142 };
143
144 let path_str = match path.to_str() {
145 Some(v) => v.to_owned(),
146 None => {
147 let msg = format!("get path str failed! path={}", path.display());
148 error!("{}", msg);
149
150 return Err(Box::<dyn Error>::from(msg));
151 }
152 };
153
154 Ok(match path_str.find(&fid) {
155 Some(_) => {
156 debug!("fid found in exe path! fid={}, path={}", fid, path_str);
157 true
158 }
159 None => {
160 warn!("fid not found in exe path! fid={}, path={}", fid, path_str);
161 false
162 }
163 })
164}
165
166static PROCESS_LOCK: once_cell::sync::OnceCell<ProcessLock> = once_cell::sync::OnceCell::new();
167
168pub fn check_process_status(service_name: &str, fid: Option<&str>) -> ProcessStatusCode {
170 if ProcessMutex::new(service_name).acquire().is_some() {
171 ProcessStatusCode::NotExists
172 } else {
173 let mut proc = ProcessLock::new(service_name);
174 let exit_code = proc.check();
175 if exit_code > 0 {
176 info!(
177 "target process in running! service={}, pid={}",
178 service_name, exit_code
179 );
180
181 if fid.is_some() {
183 match proc.check_fid(fid.unwrap()) {
184 Ok(valid) => {
185 if valid {
186 ProcessStatusCode::Running
187 } else {
188 ProcessStatusCode::RunningOther
189 }
190 }
191 Err(_e) => {
192 ProcessStatusCode::Running
194 }
195 }
196 } else {
197 ProcessStatusCode::Running
198 }
199 } else {
200 info!(
201 "target process mutex exists but plock not found! service={}, pid={}",
202 service_name, exit_code
203 );
204 ProcessStatusCode::NotExists
205 }
206 }
207}
208
209pub fn check_cmd_and_exec(service_name: &str) -> ProcessAction {
210 check_cmd_and_exec_ext(service_name, service_name)
211}
212
213pub fn check_cmd_and_exec_ext(service_name: &str, mutex_name: &str) -> ProcessAction {
214 let about = format!("{} ood service for cyfs system", service_name);
215 let app = App::new(&format!("{}", service_name))
216 .version(cyfs_base::get_version())
217 .about(&*about);
218
219 let app = prepare_args(app);
220 let matches = app.get_matches();
221
222 check_cmd_and_exec_with_args_ext(service_name, mutex_name, &matches)
223}
224
225pub fn check_cmd_and_exec_with_args(service_name: &str, matches: &ArgMatches) -> ProcessAction {
226 check_cmd_and_exec_with_args_ext(service_name, service_name, matches)
227}
228
229const CYFS_CMD_LOG_KEY: &str = "CYFS_CMD_LOG";
231
232fn load_cmd_level_from_env() -> Option<String> {
233 match std::env::var(CYFS_CMD_LOG_KEY) {
234 Ok(val) => Some(val),
235 Err(_) => None,
236 }
237}
238
239pub fn check_cmd_and_exec_with_args_ext(
246 service_name: &str,
247 mutex_name: &str,
248 matches: &ArgMatches,
249) -> ProcessAction {
250 SERVICE_NAME.lock().unwrap().init(mutex_name);
251
252 let (cmd, fid) = parse_cmd(service_name, matches);
253
254 if cmd != ProcessAction::Start {
256 let cmd_log_level = load_cmd_level_from_env();
258
259 if cmd_log_level.is_some() || matches.is_present("cmd-log") {
260 let name = format!("{}-status", service_name);
261 let level = cmd_log_level.unwrap_or("trace".to_owned());
262
263 crate::init_log(&name, Some(&level));
264 } else {
265 let console_log = matches.is_present("console-log");
266 if console_log || (cmd != ProcessAction::Start && cmd != ProcessAction::Install) {
267 if let Err(e) = simple_logger::SimpleLogger::default().init() {
268 println!("init simple logger error! {}", e);
269 }
270 } else {
271 }
273 }
274 }
275 if cmd != ProcessAction::Start {
277 std::thread::spawn(move || {
278 std::thread::sleep(std::time::Duration::from_secs(10));
279 let code = ProcessStatusCode::NotExists as i32;
280 error!("process running out of time! now will exit with {}", code);
281 crate::flush_log();
282 get_process_cmd_funcs().exit_process(cmd, code);
283 });
284 }
285
286 match cmd {
287 ProcessAction::Stop => {
288 if *CURRENT_PROC_LOCK {
289 info!("process mutex not exists! service={}", service_name);
290 get_process_cmd_funcs().exit_process(cmd, 0);
291 }
292
293 let exit_code = try_stop_process(service_name);
294 get_process_cmd_funcs().exit_process(cmd, exit_code);
295 }
296 ProcessAction::Status => {
297 if *CURRENT_PROC_LOCK {
299 info!("process mutex not exists! service={}", service_name);
300 get_process_cmd_funcs().exit_process(cmd, ProcessStatusCode::NotExists as i32);
301 }
302
303 let mut proc = ProcessLock::new(service_name);
304 let exit_code = proc.check();
305 let status;
306 if exit_code > 0 {
307 info!(
308 "target process in running! service={}, pid={}",
309 service_name, exit_code
310 );
311
312 if fid.is_some() {
313 status = match proc.check_fid(&fid.unwrap()) {
314 Ok(valid) => {
315 if valid {
316 ProcessStatusCode::Running
317 } else {
318 ProcessStatusCode::RunningOther
319 }
320 }
321 Err(_e) => {
322 ProcessStatusCode::Running
324 }
325 };
326 } else {
327 status = ProcessStatusCode::Running;
328 }
329 } else {
330 info!(
331 "target process not found! service={}, pid={}",
332 service_name, exit_code
333 );
334 status = ProcessStatusCode::NotExists;
335 }
336
337 info!(
338 "check status return, service={}, status={:?}",
339 service_name, status
340 );
341 get_process_cmd_funcs().exit_process(cmd, status as i32);
342 }
343 ProcessAction::Install => {
344 return ProcessAction::Install;
345 }
346 _ => {
347 if !*CURRENT_PROC_LOCK {
348 let msg = format!("process mutex already exists! service={}", service_name);
349 warn!("{}", msg);
350 println!("{}", msg);
351
352 get_process_cmd_funcs().exit_process(cmd, 1);
353 }
354
355 let mut proc = ProcessLock::new(service_name);
356 if let Err(e) = proc.force_acquire() {
357 let msg = format!(
358 "target process already in running! service={}, pid={}, err={:?}",
359 service_name,
360 proc.get_old_pid(),
361 e
362 );
363 error!("{}", msg);
364 println!("{}", msg);
365
366 get_process_cmd_funcs().exit_process(cmd, 1);
367 }
368
369 if let Err(_) = PROCESS_LOCK.set(proc) {
370 unreachable!();
371 }
372
373 return ProcessAction::Start;
374 }
375 }
376}
377
378pub fn check_process_mutex(service_name: &str) -> bool {
380 let mutex = ProcessMutex::new(&service_name);
381 let ret = match mutex.acquire() {
382 Some(g) => {
383 drop(g);
384 false
385 }
386 None => true,
387 };
388
389 ret
390}
391
392pub fn try_stop_process(service_name: &str) -> i32 {
394 let mut proc = ProcessLock::new(service_name);
395 let pid = proc.check();
396 if pid > 0 {
397 info!(
398 "target process in running! now will kill, service={}, pid={}",
399 service_name, pid
400 );
401
402 let exit_code = match proc.kill() {
403 true => pid as i32,
404 false => -1i32,
405 };
406
407 exit_code
408 } else {
409 info!(
410 "process in running but pid file not exists! {}",
411 service_name
412 );
413 -1
414 }
415}
416
417pub fn try_enter_proc(service_name: &str) -> bool {
419 SERVICE_NAME.lock().unwrap().init(service_name);
420
421 if !*CURRENT_PROC_LOCK {
422 info!("process mutex already exists! service={}", service_name);
423 return false;
424 }
425
426 let mut proc = ProcessLock::new(service_name);
427 if let Err(e) = proc.force_acquire() {
428 error!(
429 "target process already in running! service={}, pid={}, err={:?}",
430 service_name,
431 proc.get_old_pid(),
432 e
433 );
434
435 return false;
436 }
437
438 if let Err(_) = PROCESS_LOCK.set(proc) {
439 unreachable!();
440 }
441
442 true
443}