src/tools.rs: implement file_set_contents_full()
This commit is contained in:
		
							
								
								
									
										44
									
								
								src/tools.rs
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								src/tools.rs
									
									
									
									
									
								
							| @ -149,13 +149,25 @@ pub fn file_get_json<P: AsRef<Path>>(path: P, default: Option<Value>) -> Result< | ||||
|     }).map_err(|err: Error| format_err!("unable to parse json from {:?} - {}", path, err)) | ||||
| } | ||||
|  | ||||
| /// Atomically write a file. We first create a temporary file, which | ||||
| /// is then renamed. | ||||
| /// Atomically write a file | ||||
| /// | ||||
| /// We first create a temporary file, which is then renamed. | ||||
| pub fn file_set_contents<P: AsRef<Path>>( | ||||
|     path: P, | ||||
|     data: &[u8], | ||||
|     perm: Option<stat::Mode>, | ||||
| ) -> Result<(), Error> { | ||||
|     file_set_contents_full(path, data, perm, None, None) | ||||
| } | ||||
|  | ||||
| /// Atomically write a file with owner and group | ||||
| pub fn file_set_contents_full<P: AsRef<Path>>( | ||||
|     path: P, | ||||
|     data: &[u8], | ||||
|     perm: Option<stat::Mode>, | ||||
|     owner: Option<nix::unistd::Uid>, | ||||
|     group: Option<nix::unistd::Gid>, | ||||
| ) -> Result<(), Error> { | ||||
|  | ||||
|     let path = path.as_ref(); | ||||
|  | ||||
| @ -180,6 +192,13 @@ pub fn file_set_contents<P: AsRef<Path>>( | ||||
|         bail!("fchmod {:?} failed: {}", tmp_path, err); | ||||
|     } | ||||
|  | ||||
|     if owner != None || group != None { | ||||
|         if let Err(err) = fchown(fd, owner, group) { | ||||
|             let _ = unistd::unlink(tmp_path); | ||||
|             bail!("fchown {:?} failed: {}", tmp_path, err); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     use std::os::unix::io::FromRawFd; | ||||
|     let mut file = unsafe { File::from_raw_fd(fd) }; | ||||
|  | ||||
| @ -320,7 +339,7 @@ pub fn file_chunker<C, R>( | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| // Returns the Unix uid/gid for the sepcified system user. | ||||
| /// Returns the Unix uid/gid for the sepcified system user. | ||||
| pub fn getpwnam_ugid(username: &str) -> Result<(libc::uid_t,libc::gid_t), Error> { | ||||
|     let info = unsafe { libc::getpwnam(std::ffi::CString::new(username).unwrap().as_ptr()) }; | ||||
|     if info == std::ptr::null_mut() { | ||||
| @ -332,6 +351,25 @@ pub fn getpwnam_ugid(username: &str) -> Result<(libc::uid_t,libc::gid_t), Error> | ||||
|     Ok((info.pw_uid, info.pw_gid)) | ||||
| } | ||||
|  | ||||
| /// Change ownership of an open file handle | ||||
| pub fn fchown( | ||||
|     fd: RawFd, | ||||
|     owner: Option<nix::unistd::Uid>, | ||||
|     group: Option<nix::unistd::Gid> | ||||
| ) -> Result<(), Error> { | ||||
|  | ||||
|     // According to the POSIX specification, -1 is used to indicate that owner and group | ||||
|     // are not to be changed.  Since uid_t and gid_t are unsigned types, we have to wrap | ||||
|     // around to get -1 (copied fron nix crate). | ||||
|     let uid = owner.map(Into::into).unwrap_or((0 as libc::uid_t).wrapping_sub(1)); | ||||
|     let gid = group.map(Into::into).unwrap_or((0 as libc::gid_t).wrapping_sub(1)); | ||||
|  | ||||
|     let res = unsafe { libc::fchown(fd, uid, gid) }; | ||||
|     nix::errno::Errno::result(res)?; | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| // Returns the hosts node name (UTS node name) | ||||
| pub fn nodename() -> &'static str { | ||||
|  | ||||
|  | ||||
							
								
								
									
										185
									
								
								src/tools/process_locker.rs~
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								src/tools/process_locker.rs~
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,185 @@ | ||||
| use failure::*; | ||||
| use std::thread; | ||||
| use std::sync::{Arc, Mutex}; | ||||
| use std::os::unix::io::AsRawFd; | ||||
|  | ||||
| //use proxmox_backup::tools; | ||||
|  | ||||
| // fixme: use F_OFD_ locks when implemented with nix::fcntl | ||||
|  | ||||
| // flock lock conversion is not atomic, so we need to use fcntl | ||||
|  | ||||
| struct ProcessLocker { | ||||
|     file: std::fs::File, | ||||
|     exclusive: bool, | ||||
|     writers: usize, | ||||
| } | ||||
|  | ||||
| struct SharedLock { | ||||
|     locker: Arc<Mutex<ProcessLocker>>, | ||||
| } | ||||
|  | ||||
| impl  Drop for SharedLock { | ||||
|     fn drop(&mut self) { | ||||
|         let tid =  std::thread::current().id(); | ||||
|         let pid =  std::process::id(); | ||||
|  | ||||
|         println!("{:?}:{:?}: Drop Writerlock", pid, tid); | ||||
|  | ||||
|         let mut data = self.locker.lock().unwrap(); | ||||
|  | ||||
|         if data.writers == 0 { panic!("unexpected ProcessLocker state"); } | ||||
|  | ||||
|         if data.writers == 1 && !data.exclusive { | ||||
|              | ||||
|             let op = libc::flock { | ||||
|                 l_type: libc::F_UNLCK as i16, | ||||
|                 l_whence: libc::SEEK_SET as i16, | ||||
|                 l_start: 0, | ||||
|                 l_len: 0, | ||||
|                 l_pid: 0, | ||||
|             }; | ||||
|  | ||||
|             if let Err(err) = nix::fcntl::fcntl(data.file.as_raw_fd(), nix::fcntl::FcntlArg::F_SETLKW(&op)) { | ||||
|                 panic!("unable to drop writer lock - {}", err); | ||||
|             } | ||||
|             data.writers = 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct ExclusiveLock { | ||||
|     locker: Arc<Mutex<ProcessLocker>>, | ||||
| } | ||||
|  | ||||
| impl  Drop for ExclusiveLock { | ||||
|     fn drop(&mut self) { | ||||
|         let tid =  std::thread::current().id(); | ||||
|         let pid =  std::process::id(); | ||||
|  | ||||
|         println!("{:?}:{:?}: Drop Exclusive lock", pid, tid); | ||||
|  | ||||
|         let mut data = self.locker.lock().unwrap(); | ||||
|  | ||||
|         if !data.exclusive { panic!("unexpected ProcessLocker state"); } | ||||
|  | ||||
|         let ltype = if data.writers != 0 { libc::F_RDLCK } else { libc::F_UNLCK }; | ||||
|         let op = libc::flock { | ||||
|             l_type: ltype as i16, | ||||
|             l_whence: libc::SEEK_SET as i16, | ||||
|             l_start: 0, | ||||
|             l_len: 0, | ||||
|             l_pid: 0, | ||||
|         }; | ||||
|  | ||||
|         if let Err(err) = nix::fcntl::fcntl(data.file.as_raw_fd(), nix::fcntl::FcntlArg::F_SETLKW(&op)) { | ||||
|             panic!("unable to drop exclusive lock - {}", err); | ||||
|         } | ||||
|  | ||||
|         data.exclusive = false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ProcessLocker { | ||||
|  | ||||
|     pub fn new(lockfile: &str) -> Result<Arc<Mutex<Self>>, Error> { | ||||
|  | ||||
|         let file = std::fs::OpenOptions::new() | ||||
|             .create(true) | ||||
|             .read(true) | ||||
|             .write(true) | ||||
|             .open(lockfile)?; | ||||
|  | ||||
|         Ok(Arc::new(Mutex::new(Self { | ||||
|             file: file, | ||||
|             exclusive: false, | ||||
|             writers: 0, | ||||
|         }))) | ||||
|     } | ||||
|  | ||||
|     pub fn try_shared_lock(locker: Arc<Mutex<Self>>) -> Result<SharedLock, Error> { | ||||
|  | ||||
|         let mut data = locker.lock().unwrap(); | ||||
|  | ||||
|         if data.writers == 0 && !data.exclusive { | ||||
|             let op = libc::flock { | ||||
|                 l_type: libc::F_RDLCK as i16, | ||||
|                 l_whence: libc::SEEK_SET as i16, | ||||
|                 l_start: 0, | ||||
|                 l_len: 0, | ||||
|                 l_pid: 0, | ||||
|             }; | ||||
|  | ||||
|             if let Err(err) = nix::fcntl::fcntl(data.file.as_raw_fd(), nix::fcntl::FcntlArg::F_SETLK(&op)) { | ||||
|                 bail!("unable to get shared lock - {}", err); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         data.writers += 1; | ||||
|          | ||||
|         Ok(SharedLock { locker: locker.clone()  }) | ||||
|     } | ||||
|      | ||||
|     pub fn try_exclusive_lock(locker: Arc<Mutex<Self>>) -> Result<ExclusiveLock, Error> { | ||||
|          | ||||
|         let mut data = locker.lock().unwrap(); | ||||
|  | ||||
|         if data.exclusive { | ||||
|             bail!("already locked exclusively"); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         let op = libc::flock { | ||||
|             l_type: libc::F_WRLCK as i16, | ||||
|             l_whence: libc::SEEK_SET as i16, | ||||
|             l_start: 0, | ||||
|             l_len: 0, | ||||
|             l_pid: 0, | ||||
|         }; | ||||
|  | ||||
|         if let Err(err) = nix::fcntl::fcntl(data.file.as_raw_fd(), nix::fcntl::FcntlArg::F_SETLK(&op)) { | ||||
|             bail!("unable to get exclusive lock - {}", err); | ||||
|         } | ||||
|  | ||||
|         data.exclusive = true; | ||||
|          | ||||
|         return Ok(ExclusiveLock { locker: locker.clone() }); | ||||
|          | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn run_test() -> Result<(), Error> | ||||
| { | ||||
|  | ||||
|     let tid =  std::thread::current().id(); | ||||
|     let pid =  std::process::id(); | ||||
|  | ||||
|     let locker = ProcessLocker::new("test.flock")?; | ||||
|  | ||||
|     println!("{:?}:{:?}: try to get shared lock", pid, tid); | ||||
|     let l1 = ProcessLocker::try_shared_lock(locker.clone())?; | ||||
|     println!("{:?}:{:?}: got shared lock", pid, tid); | ||||
|  | ||||
|     println!("{:?}:{:?}: try to get exclusive lock", pid, tid); | ||||
|     let l2 = ProcessLocker::try_exclusive_lock(locker.clone())?; | ||||
|     println!("{:?}:{:?}: got exclusive lock", pid, tid); | ||||
|  | ||||
|     println!("{:?}:{:?}: try to get shared lock", pid, tid); | ||||
|     let l3 = ProcessLocker::try_shared_lock(locker.clone())?; | ||||
|     println!("{:?}:{:?}: got shared lock", pid, tid); | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| fn main() -> Result<(), Error> | ||||
| { | ||||
|     if let Err(err) = run_test() { | ||||
|         println!("ERROR: {}", err); | ||||
|     } | ||||
|  | ||||
|     let tid =  std::thread::current().id(); | ||||
|     let pid =  std::process::id(); | ||||
|     println!("{:?}:{:?}: Exit", pid, tid); | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
							
								
								
									
										47
									
								
								src/tools/procfs.rs~
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/tools/procfs.rs~
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| use failure::*; | ||||
|  | ||||
| use crate::tools; | ||||
| use lazy_static::lazy_static; | ||||
| use regex::Regex; | ||||
|  | ||||
| pub struct ProcFsPidStat { | ||||
|     pub status: u8, | ||||
|     pub utime: u64, | ||||
|     pub stime: u64, | ||||
|     pub starttime: u64, | ||||
|     pub vsize: u64, | ||||
|     pub rss: i64, | ||||
| } | ||||
|  | ||||
| pub fn read_proc_pid_stat(pid: nix::unistd::Pid) -> Result<ProcFsPidStat, Error> { | ||||
|  | ||||
|     let statstr = tools::file_read_firstline(format!("/proc/{}/stat", pid))?; | ||||
|  | ||||
|     lazy_static! { | ||||
|         static ref REGEX: Regex = Regex::new(r"^(?P<pid>\d+) \(?:.*\) (?P<status>\S) -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ \d+ \d+ \d+ \d+ \d+ (?P<utime>\d+) (?P<stime>\d+) -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ 0 (?P<starttime>\d+) (?P<vsize>\d+) (?P<rss>-?\d+) \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ -?\d+ -?\d+ \d+ \d+ \d+").unwrap(); | ||||
|     } | ||||
|  | ||||
|     if let Some(cap) = REGEX.captures(&statstr) { | ||||
|         if pid != nix::unistd::Pid::from_raw(cap["pid"].parse::<i32>().unwrap()) { | ||||
|             bail!("unable to read pid stat for process '{}' - got wrong pid", pid); | ||||
|         } | ||||
|  | ||||
| 	return Ok(ProcFsPidStat { | ||||
| 	    status: cap["status"].parse::<u8>().unwrap(), | ||||
| 	    utime: cap["utime"].parse::<u64>().unwrap(), | ||||
| 	    stime: cap["stime"].parse::<u64>().unwrap(), | ||||
| 	    starttime: cap["starttime"].parse::<u64>().unwrap(), | ||||
| 	    vsize: cap["vsize"].parse::<u64>().unwrap(), | ||||
| 	    rss: cap["rss"].parse::<i64>().unwrap() * 4096, | ||||
| 	}); | ||||
|     } | ||||
|  | ||||
|     bail!("unable to read pid stat for process '{}'", pid); | ||||
| } | ||||
|  | ||||
| pub fn read_proc_starttime(pid: nix::unistd::Pid) -> Result<u64, Error> { | ||||
|  | ||||
|     let info = read_proc_pid_stat(pid)?; | ||||
|  | ||||
|     Ok(info.starttime) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user