cyfs_util/process/
pid_lock.rs

1use 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    // From the POSIX standard: If sig is 0 (the null signal), error checking
37    // is performed but no signal is actually sent. The null signal can be
38    // used to check the validity of pid.
39    #[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    // 检查当前pid文件里面的path是否和fid匹配
106    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        // 以pid|path格式写入
237        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}