#![allow(non_camel_case_types)]
use std::io::{BufWriter, IoResult, IoError};
use std::os::errno;
use std::mem;
use libc;
use libc::{
c_void, c_int, c_short, pid_t, mode_t, size_t,
O_CREAT, O_WRONLY, SEEK_SET, EINTR, EACCES, EAGAIN
};
use nix::sys::stat;
use nix::SysResult;
use ffi::{
flock, O_SYNC, F_SETFD, F_GETLK,
F_SETLK, F_WRLCK, F_UNLCK, FD_CLOEXEC
};
pub struct File {
fd: c_int
}
macro_rules! check(
($expr:expr) => ({
let mut ret;
loop {
ret = unsafe { $expr };
debug!("ffi; expr={}; ret={}", stringify!($expr), ret);
if ret < 0 {
let err = errno() as c_int;
if err == EINTR {
continue;
}
return Err(IoError::from_errno(err as uint, false));
}
else {
break;
}
}
ret
})
)
unsafe fn setlk(fd: c_int, fl: &flock) -> c_int {
let ret = libc::fcntl(fd, F_SETLK, fl as *const flock);
if ret < 0 {
match errno() as c_int {
EACCES | EAGAIN => return 1,
_ => {}
}
}
ret
}
impl File {
pub fn open(path: &Path, create: bool, write: bool, mode: u32) -> IoResult<File> {
let mut flags = O_SYNC;
if create { flags |= O_CREAT; }
if write { flags |= O_WRONLY; }
let fd = check!(libc::open(path.to_c_str().as_ptr(), flags, mode as mode_t));
check!(libc::fcntl(fd, F_SETFD, FD_CLOEXEC));
return Ok(File { fd: fd });
}
pub fn truncate(&mut self) -> IoResult<()> {
check!(libc::ftruncate(self.fd, 0));
Ok(())
}
pub fn lock(&mut self) -> IoResult<bool> {
let mut fl: flock = unsafe { mem::zeroed() };
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET as c_short;
let ret = check!(setlk(self.fd, &fl));
Ok(ret == 0)
}
pub fn check(&mut self) -> IoResult<pid_t> {
let mut fl: flock = unsafe { mem::zeroed() };
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET as c_short;
check!(libc::fcntl(self.fd, F_GETLK, &fl as *const flock));
if fl.l_type == F_UNLCK {
Ok(0)
}
else {
Ok(fl.l_pid)
}
}
pub fn write(&mut self, pid: pid_t) -> IoResult<()> {
let mut buf: [u8, ..20] = unsafe { mem::zeroed() };
let len = {
let mut reader = BufWriter::new(buf);
try!(write!(&mut reader, "{}\n", pid));
try!(reader.tell())
};
let mut pos = 0;
while pos < len {
let ptr = unsafe { buf.as_ptr().offset(pos as int) };
let ret = check!(libc::write(self.fd, ptr as *const c_void, (len - pos) as size_t));
pos += ret as u64;
}
Ok(())
}
pub fn stat(&self) -> SysResult<stat::FileStat> {
stat::fstat(self.fd)
}
}
impl Drop for File {
fn drop(&mut self) {
debug!("closing file");
unsafe { libc::close(self.fd); }
}
}