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