ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/salaryman/trunk/src/service.rs
Revision: 7
Committed: Wed Jun 4 04:45:58 2025 UTC (4 months ago) by yuzu
Original Path: trunk/src/model.rs
File size: 5294 byte(s)
Log Message:
Work on Service::stdout() and Service::stderr()

File Contents

# User Rev Content
1 yuzu 7 use serde::{Deserialize, Serialize};
2    
3     use std::{
4     fs::canonicalize,
5     io::{Read, BufRead, BufReader},
6     path::PathBuf,
7     process::{Child, Command, Stdio},
8     sync::{Arc, Mutex},
9     };
10    
11     #[derive(Serialize, Deserialize, Clone, Debug)]
12     pub struct ServiceConf {
13     pub name: String,
14     command: String,
15     args: Option<String>,
16     directory: Option<PathBuf>,
17     pub autostart: bool,
18     }
19     impl ServiceConf {
20     pub fn new() -> Self {
21     Self {
22     name: String::new(),
23     command: String::new(),
24     args: None,
25     directory: None,
26     autostart: false,
27     }
28     }
29     pub fn from_parts(
30     name: String,
31     command: String,
32     args: Option<String>,
33     directory: Option<PathBuf>,
34     autostart: bool,
35     ) -> Self {
36     Self {
37     name,
38     command,
39     args,
40     directory,
41     autostart,
42     }
43     }
44     }
45    
46     #[derive(Debug)]
47     pub struct Service {
48     pub config: ServiceConf,
49     process: Option<Mutex<Child>>,
50     }
51     impl Service {
52     pub fn new() -> Self {
53     Self {
54     config: ServiceConf::new(),
55     process: None,
56     }
57     }
58     pub fn from_conf(config: ServiceConf) -> Self {
59     Self {
60     config,
61     process: None,
62     }
63     }
64     pub fn start(&mut self) -> Result<(), Box<dyn std::error::Error>> {
65     if self.process.is_some() {
66     return Err(Box::new(std::io::Error::new(
67     std::io::ErrorKind::AlreadyExists,
68     "process already exists!",
69     )));
70     }
71     let command: &str = &self.config.command.as_str();
72     let args: Vec<String> = if let Some(a) = &self.config.args {
73     let mut v: Vec<String> = Vec::new();
74     for arg in a.split_whitespace() {
75     v.push(String::from(arg));
76     }
77     v
78     } else {
79     Vec::new()
80     };
81     if let Some(cwd) = &self.config.directory {
82     let child = Command::new(command)
83     .args(args)
84     .current_dir(canonicalize(cwd)?)
85     .stdin(Stdio::piped())
86     .stdout(Stdio::piped())
87     .stderr(Stdio::piped())
88     .spawn()?;
89     self.process = Some(Mutex::new(child));
90     } else {
91     let child = Command::new(command)
92     .args(args)
93     .stdin(Stdio::piped())
94     .stdout(Stdio::piped())
95     .stderr(Stdio::piped())
96     .spawn()?;
97     self.process = Some(Mutex::new(child));
98     };
99     Ok(())
100     }
101     pub fn stop(&mut self) -> Result<(), Box<dyn std::error::Error>> {
102     if let Some(process) = &self.process {
103     if let Ok(mut process) = process.lock() {
104     process.kill()?;
105     } else {
106     return Err(Box::new(std::io::Error::new(
107     std::io::ErrorKind::ResourceBusy,
108     "cannot acquire lock",
109     )))
110     }
111     } else {
112     return Err(Box::new(std::io::Error::new(
113     std::io::ErrorKind::NotFound,
114     "process already exists!",
115     )));
116     };
117     self.process = None;
118     Ok(())
119     }
120     //TODO: this function needs to fork and do message passing via mpsc channels.
121     pub fn stdout(&mut self) -> Result<String, Box<dyn std::error::Error>> {
122     if let Some(process) = &mut self.process {
123     if let Ok(mut process) = process.lock() {
124     if let Some(stdout) = process.stdout.as_mut() {
125     let reader = BufReader::new(stdout);
126     Ok(String::from(reader.lines().filter_map(|line| line.ok()).collect::<String>()))
127     } else {
128     Err(Box::new(std::io::Error::new(
129     std::io::ErrorKind::ResourceBusy,
130     "cannot acquire stdout",
131     )))
132     }
133     } else {
134     Err(Box::new(std::io::Error::new(
135     std::io::ErrorKind::ResourceBusy,
136     "cannot acquire lock",
137     )))
138     }
139     } else {
140     Err(Box::new(std::io::Error::new(
141     std::io::ErrorKind::NotFound,
142     "is the process started?",
143     )))
144     }
145     }
146     //TODO: this function needs to fork and do message passing via mpsc channels.
147     //TODO: this function needs to use a bufreader instead
148     pub fn stderr(&mut self) -> Result<String, Box<dyn std::error::Error>> {
149     if let Some(process) = &mut self.process {
150     if let Ok(mut process) = process.lock() {
151     let mut s = String::new();
152     if let Some(stderr) = process.stderr.as_mut() {
153     stderr.read_to_string(&mut s)?;
154     }
155     Ok(s)
156     } else {
157     Err(Box::new(std::io::Error::new(
158     std::io::ErrorKind::ResourceBusy,
159     "cannot acquire lock",
160     )))
161     }
162     } else {
163     Err(Box::new(std::io::Error::new(
164     std::io::ErrorKind::NotFound,
165     "is the process started?",
166     )))
167     }
168     }
169     }
170