cyfs_util/process/
pid_lock.rs1use cyfs_base::*;
2
3use std::fs;
4use std::fs::File;
5use std::io::{Read, Write};
6use std::path::{Path, PathBuf};
7use fs2::FileExt;
8
9#[cfg(windows)]
10use std::os::windows::fs::OpenOptionsExt;
11
12#[cfg(windows)]
13use super::win_process::Process;
14
15
16#[derive(Debug, PartialEq)]
17pub enum PidLockError {
18 LockExists,
19 InvalidState,
20}
21
22type PidLockResult = Result<(), PidLockError>;
23
24#[derive(Debug, PartialEq)]
25enum PidlockState {
26 New,
27 Acquired,
28 Released,
29}
30
31fn getpid() -> u32 {
32 unsafe { libc::getpid() as u32 }
33}
34
35fn process_exists(pid: i32) -> bool {
36 #[cfg(not(windows))]
40 unsafe {
41 let result = libc::kill(pid, 0);
42 result == 0
43 }
44
45 #[cfg(windows)]
46 {
47 match Process::open(pid as u32) {
48 Ok(_) => true,
49 Err(_e) => false,
50 }
51 }
52}
53
54fn kill_process(pid: i32) -> bool {
55 #[cfg(not(windows))]
56 unsafe {
57 let result = libc::kill(pid, 9);
58 result == 0
59 }
60
61 #[cfg(windows)]
62 {
63 let ret = Process::open(pid as u32);
64 if let Err(e) = ret {
65 error!("open process for kill failed! pid={}, err={}", pid, e);
66 return false;
67 }
68
69 let proc = ret.unwrap();
70 match proc.kill() {
71 Ok(_) => true,
72 Err(_e) => false,
73 }
74 }
75}
76
77pub struct PidLock {
78 pid: u32,
79 pub old_pid: u32,
80 path: PathBuf,
81 state: PidlockState,
82
83 pid_file: Option<File>,
84}
85
86impl PidLock {
87 pub fn new(path: &Path) -> Self {
88 PidLock {
89 pid: getpid(),
90 old_pid: 0u32,
91 path: path.to_owned(),
92 state: PidlockState::New,
93 pid_file: None,
94 }
95 }
96
97 pub fn check(&mut self) -> u32 {
98 assert!(self.old_pid == 0);
99
100 self.check_stale();
101
102 return self.old_pid;
103 }
104
105 pub fn check_fid(&self, fid: &str) -> BuckyResult<bool> {
107 debug!("will check fid {}", fid);
108
109 let fid = fid.to_owned();
110
111 match fs::OpenOptions::new().read(true).open(self.path.as_path()) {
112 Ok(mut file) => {
113 let mut contents = String::new();
114 if let Err(e) = file.read_to_string(&mut contents) {
115 error!("read file error: {}", e);
116 return Err(e.into());
117 }
118
119 let info: Vec<&str> = contents.trim().split("|").collect();
120
121 if info.len() < 2 {
122 let msg = format!("invalid pid file format: {}", contents);
123 error!("{}", msg);
124 Err(BuckyError::new(BuckyErrorCode::InvalidFormat, msg))
125 } else {
126 let path_str = info[1].to_owned();
127 match path_str.find(&fid) {
128 Some(_) => {
129 debug!("fid found in exe path! fid={}, path={}", fid, path_str);
130 Ok(true)
131 }
132 None => {
133 warn!("fid not found in exe path! fid={}, path={}", fid, path_str);
134 Ok(false)
135 }
136 }
137 }
138 }
139 Err(e) => {
140 error!("open pid file error! file={} ,err={}", self.path.display(), e);
141 Err(e.into())
142 }
143 }
144 }
145
146 fn check_stale(&mut self) {
147 debug!("will check_stale: {}", self.path.display());
148
149 match fs::OpenOptions::new().read(true).open(self.path.as_path()) {
150 Ok(mut file) => {
151 let mut contents = String::new();
152 if let Err(e) = file.read_to_string(&mut contents) {
153 error!("read file error: {}", e);
154 return;
155 }
156
157 let info: Vec<&str> = contents.trim().split("|").collect();
158
159 match info[0].parse::<i32>() {
160 Ok(pid) => {
161 if !process_exists(pid) {
162 warn!("old process {} not exists! removing stale pid file at {}", pid, self.path.display());
163 if let Err(e) = fs::remove_file(&self.path) {
164 error!("remove file error: {}", e);
165 }
166 } else {
167 info!("old process exists: {}, {}", pid, self.path.display());
168 self.old_pid = pid as u32;
169 }
170 }
171 Err(e) => {
172 error!("parse old process pid error! {} value={:?}, err={}", self.path.display(), info, e);
173 if let Err(e) = fs::remove_file(&self.path) {
174 error!("remove file error: {}", e);
175 }
176 }
177 }
178 }
179 Err(_) => {}
180 };
181 }
182
183 pub fn kill(&self) -> bool {
184 assert!(self.old_pid > 0);
185
186 info!("will kill process: {} {}", self.path.display(), self.old_pid);
187
188 kill_process(self.old_pid as i32)
189 }
190
191 pub fn acquire(&mut self, ignore_exists: bool) -> PidLockResult {
192 match self.state {
193 PidlockState::New => {}
194 _ => {
195 return Err(PidLockError::InvalidState);
196 }
197 }
198 self.check_stale();
199
200 if ignore_exists {
201 if self.path.exists() {
202 if let Err(e) = fs::remove_file(self.path.as_path()) {
203 error!("remove old pid file error: {} {}", self.path.display(), e);
204 }
205 }
206 }
207
208 assert!(self.pid_file.is_none());
209
210 let ret;
211 #[cfg(windows)]
212 {
213 ret = fs::OpenOptions::new()
214 .create_new(true)
215 .write(true)
216 .share_mode(winapi::um::winnt::FILE_SHARE_READ)
217 .open(self.path.as_path());
218 }
219 #[cfg(not(windows))]
220 {
221 ret = fs::OpenOptions::new()
222 .create_new(true)
223 .write(true)
224 .open(self.path.as_path());
225 }
226
227 let mut file = match ret
228 {
229 Ok(file) => file,
230 Err(e) => {
231 info!("acquire pid lock failed! file={} err={}", self.path.display(), e);
232 return Err(PidLockError::LockExists);
233 }
234 };
235
236 let path = match std::env::current_exe() {
238 Ok(v) => v.to_str().unwrap().to_owned(),
239 Err(e) => {
240 error!("get current_exe failed! err={}", e);
241 "".to_owned()
242 }
243 };
244
245 let content = format!("{}|{}", self.pid, path);
246 file.write_all(&content.into_bytes()[..])
247 .unwrap();
248
249 if let Err(e) = file.lock_shared() {
250 error!("lock pid file error! file={}, err={}", self.path.display(), e);
251 return Err(PidLockError::InvalidState);
252 }
253
254 self.pid_file = Some(file);
255
256 self.state = PidlockState::Acquired;
257 Ok(())
258 }
259
260 pub fn release(&mut self) -> PidLockResult {
261 match self.state {
262 PidlockState::Acquired => {}
263 _ => {
264 return Err(PidLockError::InvalidState);
265 }
266 }
267
268 fs::remove_file(self.path.as_path()).unwrap();
269
270 self.state = PidlockState::Released;
271 Ok(())
272 }
273}